All of the code in my project is procedural. It isn't using any framework, or any MVC structure. Most of the PHP is form handlers with some logic. So lots of code like this:
<?php
require "headerFile.php";
$user = array(); // filled with user data
if (isset($_POST['submitButton'])) {
if (isset($_POST['money'])) {
if (is_numeric($_POST['money'])) { // I don't actually validate numbers this way
if ($_POST['money'] <= $user['money']) {
$query = mysql_query("SELECT * FROM someTable WHERE id={$user['id']}");
if($result = mysql_fetch_array($query)) {
if ($someOtherCheck == $user['someOtherData']) {
/*
* run some queries, give user some item
*
*/
} else {
echo "This isn't right.";
}
} else {
echo "You don't have a row in some table!";
}
} else {
echo "You don't have that much money!";
}
} else {
echo "Enter a valid number";
}
} else {
echo "Enter how much you want to wager.";
}
}
// some hard coded form goes here
require "footerFile.php";
?>
There are over a hundred of these forms and nested if handlers, some that are similar with more/fewer conditions.
I want to start using OOP and I've been reading up on design patterns but I can't seem to find anything that applicable to my project.
I'm adding loads of new blocks of code similar to the block above, I don't want to copy and paste and just change a couple of bits here and there, what are my options?
First off, if you are ever copying and pasting code that should be a BIG RED FLAG. This should be a warning that instead of copying and pasting, you need to write a generalized function that can be used.
Secondly, it's really time to stop using mysql_query-like functions. Even the PHP page says use of those functions is discouraged. I would start moving your SQL over to PDO which is more OO-like.
When I inherited an application that looked the code you posted, I wrote a blog post about the steps I took to start getting a handle on things. It may also be helpful to you - http://mattmccormick.ca/2011/10/13/how-to-refactor-a-php-application/
OOP is not always about application. It is about readability and structure. Honestly, how readable is the code you have posted here? There are a ton of things happening and it would take you 10 minutes to decipher them all. However, if you break it down into classes with class functions you would be able to glance at things and know what is going on.
OOP does not do a ton for php all the time, but its something you want to do for almost all other non-static load languages. With the added benefit that if you have more than one programmer on a project you can both read the code. Comments are you friend.
You're best option is to use functions with returns. Return the value and then the function does whatever it needs to do with it. I personally would do something like
$class->check
if error
$this->error_handler
and the function error handler does whatever you want it to do with the error, if its die or echo then do that, but you build the error handler, so if you ever want to change it, you chance it in one place instead of 20.
Even without bringing in OOP, you could do wonders for structuring your code to be readable. There's no need to nest your conditionals if they're not dependent on each other.
$form_is_valid = true;
if (!isset($_POST['submitButton'])) {
echo "This isn't right.";
$form_is_valid = false;
}
if (!isset($_POST['money'])) {
echo "Enter how much you want to wager.";
$form_is_valid = false;
} elseif (!is_numeric($_POST['money'])) {
echo "Enter a valid number";
$form_is_valid = false;
} elseif (!$_POST['money'] <= $user['money']) {
echo "You don't have that much money!";
$form_is_valid = false;
}
if ($form_is_valid) {
do_stuff_here();
}
Regarding Frameworks
A decent framework will help you on your way to organizing code better trough separation of concerns, but does not necessarily enforce best practices. Subjectively, I say it takes hands-on experience and making lots of mistakes before best practices are ingrained in your head.
Try to think of a framework as a delivery mechanism only. Optimally, your code is not tied to any one framework. This generally means using components to handle different aspects of your application such as routing, validation, config, events, dependencies, etc.
Regarding OOP
Something I feel you should start with would be the SOLID principle. This will help you, although not guarantee, avoid making mistakes that cost you a lot of time down the road.
Foremost, your objects should only have a single responsibility. e.g. a Product object should not be responsible for acting as a data store, persisting itself, handling an order, etc etc.
Also another biggy would be dependency injection. This is huge for unit testing your classes (which you should get in the habit of doing). In a nutshell, do not construct dependency within a class. Construct it beforehand and pass it in through either a constructor argument or a setter method.
The process of architecting an application merits having its own books, so I'm not going to attempt writing it all here. If you follow the SOLID principle though, you will be well on your way to being a better developer.
Regarding your code above
That kind of nesting is generally a smell, and I can see why you are asking the question...
Step 1 would be to separate the validation in to its own service. Thinking of this in a MVC sense, at the least, your controller would only have [pseudocode] if ($form->isValid()) { do something }. This alone eliminates the bulk of spaghetti you have.
I want to start using OOP and I've been reading up on design patterns but I can't seem to find anything that applicable to my project.
You don't have to start with patterns yet .. understand the basics and you can progress form there ..
This is a basic example
if (isset($_POST['submitButton'])) {
$db = new \mysqli("localhost", "user", "pass", "db");
$user = new User("Juddling Stack", 123456);
$user->setMoney(500.01);
$player = new PlayerRegister($db, $user);
if (! isset($_POST['money']) || ! is_numeric($_POST['money']))
$player->addError("Enter Valid Wager Money");
if ($_POST['money'] <= $user['money']) {
$player->addError("You don't have that much money!");
}
try {
$player->doSQL();
} catch ( BaseException $e ) {
foreach ( $e->getError() as $error ) {
echo $error, " <br />";
}
}
}
Classes Used
It looks like your code is combining certain components of both the model and view layers. You're running queries against a database, and in the same place, you're including hard coded forms. So, a good place for you to start would be to split these tasks up into two separate classes. For example, write a class to connect to your database and run queries against it, and another class to actually serve your content.
My advice on Design patterns is to not get too bogged down in the details. Instead, try to understand why certain patterns are so helpful, and what problems they are trying to solve. A lot of beginners get too bogged down in the how , and they end up wasting so much time learning the nuances of a framework when a simple solution would have sufficed.
Finally, as you read through your code, keep an eye out for things that could potentially be structured into a class. Also, remember that specialization is key, and by building classes that are very specialized in what they do, you are building modular components that could potentially be used in other projects,
Let's start by stating the problem you are having which is not not using OOP, but called programming-overhead or in programmer terms spaghetti-code.
If you experience a lot of overhead, meaning time wasted writing almost the exact same line of code, where only it's content is different, but the functionality is the same. Then start slicing every peace of code that's the same from it's function, but differentiate its content.
You stated there is more to be copied pasted, and even more complex, I'll just do the form validation part (something I called stage 1), which is just one simple example of how to apply logic that does all the work for you by feeding input it expects. One example might be more elegant than others.
all code below is not tested
An example for locating code of equal functionality.
// functionality on checking values is the same, but it's content is not
if (isset($_POST['submitButton'])) {
if (isset($_POST['money'])) {
if (is_numeric($_POST['money'])) {
and
// though every decision made by its content is only being produced once ..
} else
echo "You don't have that much money!";
} else
echo "Enter a valid number";
} else
echo "Enter how much you want to wager.";
Now the trick is to find a solution to logically solve this. PHP is full of build-in functions, but first grasp the idea on how to solve it. An example would be to have each key, like submitButton, money have a value that would equal to not exists if not set/present, let's say null. Second, you are comparing values with a key supplied by the $_POST, so no matter what.. your $_POST array is the decision maker.
A keen example can be seen on how the jQuery library has been build using the $.extend() method to apply default values to keys, so that a key always has a value and always a decision to make by not checking first if it exists. But so can PHP.
Let's default the values.
$_POST = array_merge(array(
'submitButton' => null,
'money' => 0,
'etc' => '...'
, $_POST);
Building a function to validate this array is a lot easier now, because you can always depend on a value being present.
You stated you have many more forms that need validation, a function that would validate certain fields would be next to figure out.
A formal representation of a valid or invalid form could be an array, e.g.
$valid_form = array(
'submitButton' => array('not_null'),
'money' => array('not_null','int'),
'etc' => '...'
);
A function to validate would be
function validateForm($values, $valid) {
// error array to be returned
$error = array();
// let's iterate over each value, remember we defaulted the $_POST array with
// all the fields it can have, so all fields should be iterated apon.
foreach($values as $key => $value) {
if(array_key_exist($key, $valid)) {
// logic content can be used by different functions, switch
// used here for simplicity
foreach($valid[$key] as $validation) {
switch($validation) {
case 'not_null':
if(is_null($value)) {
$error[] = "error logic";
continue; // skip rest
}
break;
case 'etc':
$error[] = "..";
break;
}
}
}
}
return $error ? $error : true; // true being valid
}
The error handling can be done in a lot of ways, just one simple example (depends on how extensive this project is going to be), you could bind the error content to the validation key, e.g.
$vfe = $valid_form_errors = array( // $vfe for simlicity's sake
'__no_error' => 'no error present for "%key%" validation',
'not_null' => '%key% should not be null',
'int' => '%key% expects to be an integer'
);
$valid_form = array(
'submitButton' => array('not_null'),
'money' => array('not_null','int'),
'etc' => '...'
);
A function to create a formal error message
function error_msg($key, $validation) {
global $vfe;
// error exists?
$eE = array_key_exists($validation,$vfe);
return str_replace('%key%', $eE?$key:$validation, $vfe[$eE?$validation:'__no_error']);
}
And in the simple switch, the error logic is
foreach($valid[$key] as $validation) {
switch($validation) {
case 'not_null':
if(is_null($value))
$error[] = error_msg($key, $validation);
break;
case 'etc':
$error[] = "..";
break;
}
}
So how would your code look like using a different logic?
// first stage ..
$form_valid = validateForm($_POST, $valid_form);
if ($form_valid === true) {
// second stage, same logic be applied as with validateForm, etc.
if($_POST['money'] <= $user['money']) {
$query = mysql_query("SELECT * FROM someTable WHERE id={$user['id']}");
if($result = mysql_fetch_array($query)) {
// third stage, same logic can be applied here..
if ($someOtherCheck == $user['someOtherData']) {
} else {
echo "This isn't right.";
}
} else {
echo "You don't have a row in some table!";
}
}
else {
$errors = $form_valid;
// error handling here
print_r($errors);
}
It all comes to how specific you can define values being expected. You can extend every function to more specific, like binding the errors to a form key so you can specifically target that input at a later stage. They key thing is to erase all possible duplication where a function might do all this for you by simply asking him to compare values you expect, and let him tell which value there actually are.
Related
Six years ago I started a new PHP OOP project without having any experience so I just made it up as I went along. Anyway, I noticed that my rather powerful mySQL server sometimes gets bogged down far too easily and was wondering what the best way to limit some db activity, when I came up with this, as an example...
private $q_enghours;
public function gEngHours() {
if ( isset($this->q_enghours) ) {
} else {
$q = "SELECT q_eh FROM " . quQUOTES . " WHERE id = " . $this->id;
if ($r = $this->_dblink->query($q)) {
$row = $r->fetch_row();
$r->free();
$this->q_enghours = $row[0];
}
else {
$this->q_enghours = 0;
}
}
return $this->q_enghours;
}
This seems like it should be effective in greatly reducing the necessary reads to the db. If the object property is populated, no need to access the db. Note that there are almost two dozen classes all with the same db access routines for the "getter". I've only implemented this change in one place and was wondering if there is a "best practice" for this that I may have missed before I re-write all the classes.
I'd say that this question is rather based on wrong premises.
If you want to deal with "easily bogged down" database, then you have to dig up the particular reason, instead of just making guesses. These trifle reads you are so concerned with, in reality won't make any difference. You have to profile your whole application and find the real cause.
If you want to reduce number of reads, then make your object to map certain database record, by reading that record and populating all the properties once, at object creation. Constructors are made for it.
As a side note, you really need a good database wrapper, just to reduce the amount of code you have to write for each database call, so, this code can be written as
public function gEngHours() {
if ( !isset($this->q_enghours) ) {
$this->q_enghours = $this->db->getOne("SELECT q_eh FROM ?n WHERE id = ?", quQUOTES, $this->id);
}
return $this->q_enghours;
}
where getOne() method is doing all the job of running the query, fetching row, getting first result from it and many other thinks like proper error handling and making query safe.
I know this is more of PHP problem because of it's loose Typing of arrays but I see this problem all over the place in a project I took over and not sure of the best way to refactor it. Suppose you have two sets of data, both multi dimensional arrays, $results_by_entity and $target_limits and we want to check what the target is foreach result_by_entity so we can set some state
foreach ($results_by_entity AS $result_by_entity) {
foreach ($target_limits AS $target_limit) {
if ($target_limit['activity_id'] == $result_by_entity['activity_id']) {
$result_by_entity->target = $target_limit->quantity;
$result_by_entity->progress = $target_limit->score;
}
}
}
There are a couple of main problems here
1-The data is really strongly tied together, so it is really hard to refactor $results_by_entity into it's own class and $target_limits into it's own class
2-The time taken to process this grows exponentially as the data size grows
I read the Refactoring book by Martin Fowler and it was really helpful but this style of problem doesn't really show up I think mostly because his examples are in JAVA which is strongly typed. The class is super run on so really hard to debug and extend but all the data is so tied together primarily because of these types of loops so not to sure how to solve. Any recommendations would be really appreciated
What you want is to index your data pre-emptively if possible:
$results_index = array();
foreach ($results_by_entity AS $result_by_entity) {
//Index this value
$results_index[$result_by_entity['activity_id']] = $result_by_entity; //Add a & in front if it's a scalar value, but it looks like it's an object in your example
}
foreach ($target_limits AS $target_limit) {
//Find the corresponding activity id in results
if (isset($results_index[$target_limit['activity_id']])) {
$result_by_entity = $results_index[$target_limit['activity_id']];
$result_by_entity->target = $target_limit->quantity;
$result_by_entity->progress = $target_limit->score;
}
}
I'm a big fan of Doctrine, but in the past have primarily used it for an easier and more OO way of accessing my database. Lately though I've found myself wanted to do MORE with the models though, to make my code more semantic or logical, and easy to use.
For an example, let's use a Stack Overflow Question and corresponding Answers. A Question can contain one or more Answers. In the past, I might do something like this (everything below is sudo code btw):
/*
* METHOD ONE: Note that this is IMO not that semantic or OO
*/
Class QuestionController{
//.......
public function addAnswer($whichQuestionId, $answerTitle, $answerCopy, $answerAuthorId)
{
//Look, I'm validating stuff here in my controller action
if(!strlen($answerTitle) || !strlen($answerCopy) || !strlen($answerAuthorId))
throw new \Exception('Invalid answer stuff!!');
//Notice here I'm actually doing some DB quering with
$author = $entityManager->find('\StackOverflow\User', $answerAuthorId);
if($author->getIsBanned())
throw new \Exception('This is user can not post answer, they are banned!');
//OK, things are valid, now we're going to load then question and create a new answer
$question = $entityManager->find('\StackOverflow\Question', $whichQuestionId)
$answer = new \StackOverflow\Answer;
$answer->setAuthor($author);
$answer->setTitle($answerTitle);
$answer->setContent($answerCopy);
$question->addAnswer($answer);
//Let's pretend we persisted everything correctly...
//Done!
}
//.......
}
Class \StackOverflow\Answer{
//Normal auto generated stuff here
}
Class \StackOverflow\Qusetion{
//Normal auto generated stuff here
}
OK, so note that I'm doing all of my validation in an controller action, which works fine, but if I want to add a answer to a question in a different controller all together then that's not very dry. I could put this validation logic in a "helper" of some sort, but that doesn't seem to be particularly OO either.
I'd RATHER do something like this (not totally well though out, but hopefully you get the idea)
/*
* METHOD TWO: Hopefully a bit more OO...
*/
Class QuestionController{
public function addAnswer($whichQuestionId, $answerTitle, $answerCopy, $answerAuthorId)
{
$result = new \stdObject();
try{
$question = $entityManager->find('\StackOverflow\Question', $whichQuestionId);
$answer = new \StackOverflow\Answer;
$answer->setAuthor($author);
$answer->setTitle($answerTitle);
$answer->setContent($answerCopy);
//THIS IS NEW!
$answer->validate();
//note that perhaps to take this a step futher, in the \StackOverflow\Question class
//I might actually call the validate function, so it's a bit more transaparent
$question->addAnswer($answer);
//Let's pretend we persisted everything correctly...
//Done!
$result->success = true;
$result->message = 'New answer added!';
}
catch(Exception $e)
{
$result->success = false;
$result->message = $e->getMessage();
}
return json_encode($result);
}
}
Class \StackOverflow\Answer{
//ALL NORMAL AUTO GENERATED GETTERS/SETTERS/ETC HERE
//CUSTOM FUNCTION - perhaps called by one of the LifeCycle callback things that Doctrine has?
public function validate()
{
//Look, I'm validating stuff here in my controller action
if(!strlen($this->getTitle()) || !strlen($this->getContent()) || !strlen($this->getAuthor()))
throw new \Exception('Invalid answer stuff!!');
//Notice here I'm actually doing some DB quering INSIDE of another model
$author = $entityManager->find('\StackOverflow\User', $answerAuthorId);
if($author->getIsBanned())
throw new \Exception('This is user can not post answer, they are banned!');
}
}
Class \StackOverflow\Question{
//Normal auto generated stuff here
}
So this is perhaps a bit better I suppose, is this the correct way to do things with Doctrine? Is it OK to call other models within an existing model, or to otherwise use the Doctrine Entity Manager in this way? Is there a cleaner way of doing this?
This specific example is pretty simple, but in real life I have a relationship similar to this but the "Answer" has much more complex and ugly validation, requiring lookups to other tables, getting information about the "Question" for it's validation, etc. It also might update the "User" in our example, for instance adding a new Answer to a Question might update the User's "QuestionsAnswered" value. I'd rather this happen transparently, without having to know it's really happening. I'd like to simply add an answer to a question, and all of the validation and cascading changes happen automatically behind the scenes.
There is no right or wrong answer to this. Its ultimately your choice as to how heavy your models in comparison with controllers.
My suggestion is to pick one way to do it, and stay consistent.
I would make a note that the validation should be related to the object that you are working with. Checking if a user is banned within the answer validation is probably not the best place to do that. Instead you should be checking that data related to the answer is valid.
I'm currently working on a project thats means I'm dealing with lots of dates and I have one view where a user can see an overview of various events, for the coming week.
The view shows multiple events for multiple accounts.
The problem I am facing is that my view has some pretty complex IF statements (checking the current date, checking the event date, checking the account id against the event etc) and its starting to look messy and I'm sure it could be done easier/more streamlined!
How would I go about moving an IF statement to the controller?
I tried looking up best practices for CI but nothing useful comes up.
Any insight would be highly appreciated!
i would have a seperate function (within the controller or a library) that is used to prepare the data. this function only needs to return an array so that the data can be constructed in the HTML without the need for all the conditional checks.
This is an example of a data prepration library, it won't just work so you will need to create a full library, it is just to give you a general idea.
<?php
class prepare {
function prep_page_data($page_id) {
$output = array();
$query = $this->ci->db->get_where("page_data", array("page_id" => $page_id));
foreach($query->result_array() as $row) {
$output = $row;
//complete all addtional checks below
if($row['datetime'] < date("Y-m-d 00:00:00")) {
$output['archive'] = "old"
}
else {
$output['archive'] = "new";
}
//you can add as many conditions as you like to complete the data collection.
}
return $output;
}
}
?>
I'm been a PHP developer for quite some time now but until today I've not found a simple way to process (aka normalize, sanitize, validate, populate and display forms and it's respective field errors).
I know that most of the PHP frameworks nowadays make this job easier but somehow I don't feel like porting all my code to one of these frameworks, and I can't quite understand how the form validation is implemented in Django for instance (I know, it's Python but I really like their approach), so I though the best way would for me to post here the way I process a simple form and maybe you guys can point me in the best direction.
<?php
// sample controller
class _sample extends framework
{
// sample action
function contact()
{
if ($this->Is->Post() === true)
{
$errors = array();
if ($this->Is->Set($_POST['name']) === false)
{
$errors['name'] = 'Please fill in your name.';
}
if (($this->Is->Email($_POST['email']) === false) || ($this->Is->Set($_POST['email']) === false))
{
$errors['email'] = 'Please fill in your email address.';
}
if (($this->Is->Phone($_POST['contact']) === false) && ($this->Is->Mobile($_POST['contact']) === false))
{
$errors['contact'] = 'Please fill in your phone (or cell phone) number.';
}
if ($this->Is->Set($_POST['message']) === false)
{
$errors['message'] = 'Please type a message';
}
// no errors, it's valid!
if (empty($errors) === true)
{
// do stuff and redirect to "success" / "thank you" page
}
// load the form view, and let it display the errors
// automatically prefill fields with $_POST values
else
{
$this->View('contact_form', $errors);
}
}
// load the form view for the first time
else
{
$this->View('contact_form');
}
}
}
?>
As you can see, this is supposed to be a simple contact form however it takes the life out of me to validate it, I have been looking into some design patterns (Observer, Factory) but I do not feel confident if and in what way I should implement them.
IMHO, the attempt to treat a form as a single concept is a failure. If you have any kind of layered architecture to your application, forms are likely to cut across these. Forms have application logic (controller layer), they have a visual representation (view layer), they have state (application layer model), and in the end they usually invoke some kind of transactional script (model layer).
I think you're much better off dropping the idea of a "form" as an entity and instead focus on the three parts (input processing, rendering and model layer) as completely separate matters, that just might (or might not) happen to be closely related to each other. This is some times referred to as the MVC pattern, although the term is so strongly loaded by now that it could mean a lot of things.
You could make an abstract base class for all your forms, classes for fieldtypes, and a static class just for validating the values of various types (validateString, validateHtml, validateEmail, validateNumber, date, etc, just the methods..). Defining your form, you would define what field objects would it use, so the Form->validate() method would invoke the Field->validate() and return the filtered value or error message. Specify default error messages for the fields, but give an option to override it when defining fields in your form class.
Oh, and leave that $_POST thing. Read the post once, pass it once to the form validation and then work on filtered field values.
Another thing is there are various ways to achieve form validation depending on your needs and the architecture of your applications, it can be hard to make an all-purpose form validator when you have various approaches to your application design. Choose a way of doing your work and stick to it (regardless if it's a ready to go framework or your own code), or whatever super-duper-form-validation you write, it will make no sense in latter projects.
One more: like Django? Good! So start programming Python in Django, you'll really change the way of thinking how to get your job done.
I know it's something you excluded, and me too was like you up until a year ago, when I forced myself to learn something obscure like Qcodo (php framework), and lo and behold, I can't do anything without it noways. It's simply wonderful to take off lots of always repeating burden off your shoulders.
Why Qcodo? At that time I wanted to learn the most advanced one since I'm already learning, so I looked for the most broad feature set, which Qcodo seemed to offer. Today I don't know which one is the hottest, but for me Qcodo is still fulfilling all my needs.