Confused about OOP and MVC

Hello all,

I’ve been learning and practising PHP for a few months now after following a series of online tutorials and I’ve been okay with it so far, however I haven’t made an application using OOP yet and I’m confused on how to design an application with classes and objects. The way I was taught was to use a sort of MVC structure where there is a file with functions to interact with a database, then to have controller code on each page to handle things like GET variables and then to have view code in include files to contain HTML output.

That’s how I was taught from an online code school. Unfortunately they don’t have an in depth tutorial on OOP, only very basic things like OOP syntax. So I had a search around on Google to try and find some tutorials and I think I have the basics of OOP figured out, but I can’t find any good examples of how to design a practical application using OOP.

I’m really just confused on how it all ties in with MVC. Do I have to use an MVC pattern when using objects and classes? Do I put stuff like view code in classes? Do I use MVC with OOP and if not, how should I design an application? Really need some help here :frowning:

If anyone has any suggestions or examples they could give me I’d really appreciate it.

Thanks,
Adam

You don’t have to use neither of them, it all comes down to the application you are writing and how you are writing it.

I myself prefer an OOP approach using some application structure/pattern like MVC. Both because it’s logic that many programmers are familiar with, and since I like the natural dependency injection/modularization you get.

If you consider this controller:

[php]<?php

class UserController {

private $model;
private $request;
private $storage;
private $view;

public function __construct(Request $request, Storage $storage, View $view) {
$this->model = new UserModel;
$this->request = $request;
$this->view = $view;
}

public function create() {
  if ($this->request->method === 'POST') {
    $user = new UserEntity;
    $user->setName($this->request->data->name)
         ->setPassword($this->request->data->password);

    if ($this->model->create($user)) {
      $this->request->redirect('user/profile.html');
    }

    $this->view->set('errors', $this->model->getErrors());
  }

  $this->view->render('user/register.html', [
     'name' => $this->request->data->name,
     'password' => $this->request->data->password
  ]);
}

}[/php]

Here we just inject different parts of our logic into the controller.

The controller does not know:

[ul][li]How we get the request method/data[/li]
[li]What a user entity does with its properties (ie hashing password)[/li]
[li]How the model saves the user[/li]
[li]Where the model saves the user[/li]
[li]How the request class redirects us somewhere[/li]
[li]How views work / are rendered[/li]
[li]etc…[/li][/ul]

So why is this good? Because now we can just swap out big parts of our system if we need to.

Say we have a simple view class that sends the 2. param (array) into a .php view file matching the 1. param. If we want to change to use a template engine (ie Twig), we can just set up Twig where we bootstrap the application and inject that class instead of our old view class. At worst write a view engine (class with a couple of methods) that takes our format and converts it to what twig expects.

[hr]

The same idea can be used throughout the application.

The model may get a $storage model injected, and can then just pass the entity it want to store over to the storage model. That way if we ever want to change where/how we store data from DB to API, CSV file or whatever else, we can just change the storage model and all the logic should work.

Here the model does not know what type of storage it uses:

[php]<?php

class UserModel {

private $storage;

public function __construct(Storage $storage) {
$this->storage = $storage;
}

public function create(UserEntity $user) {

 if (!$this->acl->canCreate($user) {
   throw new AccessDeniedException();
 }

 if (!$this->validate($user)) {
   throw new InvalidFormDataException($this->getLastValidationErrors());
 }

 return $this->storage->save($user);

}

}[/php]

We can get some control though. By adding the type we expect in the constructor we do know it extends our interface “Storage”, so we know it contains the necessary methods and return the types we need.

Using interfaces to enforce the layout of different parts of the system will allow us to change them easily in the future as their “core” functionality should be the same (same methods/params/returns).

[hr]

MVC is actually just an application structure pattern. It says to split up the code into the following parts:

Model
Main application logic

View
Simple template files
Should be “blind” from the rest of the application
Can only see the params it has been passed by the controller

Controller
Gets user input (requested urls, get/post params, etc)
Passes the input on to the model where the main application logic is
Shows a response to the user

[hr]

I suggest you try out a couple of simple PHP frameworks. Like Flight, Slim, Fat Free Framework. They all use an OOP approach to building MVC applications.

Note that many frameworks (including the above) use a front controller that bootstraps the application and does the routing. This means all requests to the web server are sent to index.php, and there the routing takes care of which “real” controller/method is initiated.

This is how it’s done in Fat Free Framework:

[php]$f3 = require(‘path/to/base.php’);

$f3->route(‘GET /user/profile.html’,‘UserController->profile’);

$f3->run();[/php]

All requests to yoursite.com/user/profile.html will initiate the profile method in the UserController.

Nifty :slight_smile:

What’s really nice about OOP is that you can have a database class do all the heavy lifting for you.

For example I have an iCRUD class (Create, Read, Update, and Delete) and in my read portion of the class I have the following:

[php]// Function for returning information about the current object:
public function read(array $data = NULL) {
$db = Database::getInstance();
$pdo = $db->getConnection();
$this->query = ‘SELECT userId, username, password, security_level, first_name, last_name, email FROM users WHERE username=:username’;
$this->query_params = [’:username’ => $data[‘username’]];

try {
  $this->stmt = $pdo->prepare($this->query);
  $this->result = $this->stmt->execute($this->query_params);
} catch (Exception $ex) {
  die("Failed to run query: " . $ex->getMessage());
}

$this->stmt->setFetchMode(PDO::FETCH_CLASS, 'Users');
$this->user = $this->stmt->fetch();
if ($this->user) {
  $this->loginStatus = password_verify($data['password'], $this->user->getPassword());
  unset($data['password']); // Unset the password:
  $this->user->clearPassword();
}

if ($this->loginStatus) {
  return $this->user;
}

}[/php]

Notice I have this in the script
[php]$this->stmt->setFetchMode(PDO::FETCH_CLASS, ‘Users’);[/php]

Well here’s the class called Users:
[php]<?php
class Users {
private $userId = NULL;
private $username = NULL;
private $password = NULL;
private $security_level = NULL;
private $first_name = NULL;
private $last_name = NULL;
private $email = NULL;
private $date_updated = NULL;
private $date_added = NULL;

public function getUserId() {
return $this->userId;
}

public function getUsername() {
return $this->username;
}

public function getPassword() {
return $this->password;
}

public function clearPassword() {
$this->password = NULL;
}

public function getSecurityLevel() {
return $this->security_level;
}

public function getFirstName() {
return $this->first_name;
}

public function getLastName() {
return $this->last_name;
}

public function getEmail() {
return $this->email;
}

public function getDateUpdate() {
return $this->date_updated;
}

public function getDateAdded() {
return $this->date_added;
}

}
[/php]

Notice it would be pretty hard for someone to access the users data and the only way I can see that happening is if I wrote in the class some sloppy code or that person is some kind of super hacker and in that case that person probably wouldn’t be messing with my website(s) but someone further up (or is it down?) the food chain. ;D

Basically all the grunt work is done behind the scene and the visitors (users) of the website will never see that portion. To me OOP keeps things nice and tidy and the one thing that I’m learning slowly about OOP is don’t try do everything at once in a class. As I’m starting to branch off and start using namespaces, I’m finding out that a class should only perform one major task, but that means more files. However, that is why there is directories and naming conventions. :wink: If you get start getting into OOP, the one of the first things to do in my opinion is develop an autoloader. That way you just develop a class and throw it in the appropriate directory. You’re then ready to go for there is no fuss or muss. As for me I think of MVC as a concept of a foundation for a framework (a blueprint) - Model, View and Controller. I’m pretty good with M & C part myself, but I tend to get lazy with View portion. :smiley: As you go along you tend to make modifications to the blueprint, though sometimes at a cost of a headache or two. :stuck_out_tongue:

Still not very clear on how to structure an application using OOP, do controllers have to be in their own classes? I’m used to just putting some controller code in each page e.g. if a user is searching for a product ill have something like this:

products.php

[php]

<?php require("functions.php"); //controller code if ($_POST) { $search_term = $_POST['search']; $products = get_products_search($search_term); } ?>

[/php]

  <!DOCTYPE html>
    <form method="post">
      <input type="text" name="search">
      <button type="submit">Search</button>
    </form>
    <ul>
      <?php
        //view code, usually in an include file
        foreach($products as $product) {
           echo '<li>' . $product['product_name'] . '</li>';
        }
      ?>
    </ul>
  </html>

functions.php

[php]

<?php //model code function get_products_search($search_term) { //database query here to get products matching search term //return an array of matching products } ?>

[/php]

That’s the sort of layout I would have for an application, a file containing functions with controller and view code in each page. It’s how I was taught so I’m assuming it’s okay, but I have no idea how to go from this to OOP, is it the same sort of thing except I use classes instead of functions? Could you maybe give me an example of how you would build a basic application using OOP?

Thanks,
Adam

Having a bit of a hard time figuring out what this iCrud class is. It sounds very generic but the code looks very specific to a the user class. It seems like it (at least in this example) should have been split up more to separate the database and the user.

You don’t have to, you get an excellent autoloader for free if you use composer (and you should) :slight_smile:

As said, try to build a couple of test applications using some already existing frameworks. The docs/examples they have should also give an impression on how you can structure it.

That code using FlightPHP and Doctrine2 ORM could look something like this:

[php]<?php // index.php

require ‘vendor/autoload.php’;

$app = new flight\Engine();

/*

  • Doctrine2 ORM
  • http://www.doctrine-project.org/
    */
    $dbParams = [
    ‘driver’ => ‘pdo_mysql’,
    ‘user’ => ‘root’,
    ‘password’ => ‘root’,
    ‘dbname’ => ‘database’,
    ‘charset’ => ‘UTF8’
    ];

// Our app should use the generated entities
$dbConfig = Doctrine\ORM\Tools\Setup::createAnnotationMetadataConfiguration([DIR . ‘/Entity’], true, null, null, false);
$dbConfig->setEntityNamespaces([‘Entity’ => ‘Entity’]);

$dbConfig->setProxyDir(DIR . ‘/Cache’);
$dbConfig->setAutoGenerateProxyClasses(false);

/*

  • Memcached
  • Free & open source, high-performance, distributed memory object caching system
  • http://www.memcached.org/
    */
    $memcached = new Memcached();
    $memcached->addServer(‘127.0.0.1’, ‘11211’, 100);
    $memcachedDriver = new \Doctrine\Common\Cache\MemcachedCache();
    $memcachedDriver->setMemcached($memcached);
    $memcachedDriver->setNamespace(‘invoice’);

$dbConfig->setQueryCacheImpl($memcachedDriver);
$dbConfig->setMetadataCacheImpl($memcachedDriver);
$dbConfig->setResultCacheImpl($memcachedDriver);

$app->register(‘db’, [‘Doctrine\ORM\EntityManager’, ‘create’], [$dbParams, $dbConfig]);

/*

  • Routes
    */
    $app->route(‘GET|POST /products/search.html’, function() use($app) {
    $controller = new Controller\Product($app, $app->request(), $app->db());
    $controller->search();
    });

/*

  • Start
    */
    $app->start();[/php]

[php]<?php // Controller/Product.php

namespace Controller;

class Product {

private $app;
private $request;
private $db;

public function __construct($app, $request, $db) {
$this->app = $app;
$this->request = $request;
$this->db = $db;
}

public function search() {
$vm = [];
if($this->request->method === ‘POST’) {
$productRepo = $this->db->getRepository(‘Repository\Product’);
$vm[‘products’] = $productRepo->findBySearch($this->request->data->search);
}

$this->app->render('Views/products/search.php', $vm);

}

}[/php]

[php]<?php // Repository/Product.php

namespace Repository;

use Doctrine\ORM\EntityRepository;

class Product extends EntityRepository {

public function findBySearch($search) {
$qb = $this->_em->createQueryBuilder();
$qb->select([‘p’])
->from(‘Product’, ‘p’)
->where($qb->expr()->orX(
$qb->expr()->like(‘p.title’, ‘?0’),
$qb->expr()->like(‘p.description’, ‘?0’)
))
->setParameters([’%’ . $search . ‘%’]);

$products = $qb->getQuery()
               ->useResultCache(true, 60*60*24, 'product_search' . $search)
               ->getResult();

return $products;

}

}[/php]

[php]<?php // Views/products/search.php
require ‘…/header.php’;
require ‘…/menu.php’;
?>

Search <?php require '../footer.php'; [/php]

ps: Symfony framework and the Doctrine ORM is built so that the part of the model belonging to your applications entities (db tables really) belong in so called repositories. Symfony is a huge framework and may be just too much to figure out at the beginning. I’d begin with one of the slim frameworks I mentioned, perhaps move up to Laravel. Symfony is worth a try, but many believe it’s too bloated, and it’s definitely very slow (one of the slowest web frameworks available). But it really has a impressive feature list.

Sorry I should had written that better, I have a tendency sometimes to not make myself clear. What I should had written was "If you get into OOP, you should use an autoloader (either develop or already made…ie framework or library). Sorry about that again.
As for iCrud, very simple. All it does is forces you to use structure and this might be the reason people tend to use abstract classes more. Abstract classes give you more flexibility. Think of this way A dog is an animal, but not every animal is a dog or a Golden Retriever is a dog, but not every dog is a golden retriever. Meaning you can have methods(functions) that actually perform some task within an abstract class, just as long as you have an abstract method. Anyway getting back to iCrud, the i in iCrud is just an interface.

Here is the interface for the the loginClass:
[php]<?php
namespace calendar_project\database;

/* The iCrud interface.

  • The interface identifies four methods:
    • create()
    • read()
    • update()
    • delete()
      */
      interface iCrud {

public function create($data);

public function read();

public function update($data);

public function delete($id=NULL);

}
[/php]

It’s also the interface for my forums (pages) class, it’s just telling me that these methods (create, read, update & delete) have to be in those classes. Hence the name interface for it is helping controlling the traffic between the various databases.

Here’s my LoginClass in full
[php]<?php
namespace calendar_project\database;
use PDO;
class LoginClass implements iCrud {

protected $query = NULL;
protected $stmt = NULL;
protected $result = NULL;
protected $query_params = NULL;
protected $row = NULL;
protected $loginStatus = false;
public $user = NULL;

// Constructor takes an array of data:
public function __construct(array $data = NULL) {
if (is_array($data)) {
$db = Database::getInstance();
$pdo = $db->getConnection();

  /* Secure the Password */
  $data['password'] = password_hash($data['password'], PASSWORD_BCRYPT, array("cost" => 15));
  
  /* Set the query variable */
  $this->query = 'INSERT INTO users (username, password, confirmation_code, security_level, first_name, last_name, email, date_added) VALUES (:username, :password, :confirmation_code, :security_level, :first_name, :last_name, :email, NOW())';

  /* Prepare the query */
  $this->stmt = $pdo->prepare($this->query);
  try {
    $this->stmt->execute(array(':username' => $data['username'], ':password' => $data['password'], ':confirmation_code' => $data['confirmation_code'], ':security_level' => $data['security_level'], ':first_name' => $data['first_name'], ':last_name' => $data['last_name'], ':email' => $data['email']));
  } catch (PDOException $error)
  {
    // Check to see if name is already exists:
    $errorCode = $error->errorInfo[1];
    if ($errorCode == MYSQL_ERROR_DUPLICATE_ENTRY) {
      error_log("Duplicate Name was Enter", 1, "[email protected]");
    } else {
      throw $error;
    }
  }
}

}

// End of constructor.
// This method also takes an array of data:
public function create($data) {
self::__construct($data);
}

// Function for returning information about the current object:
public function read(array $data = NULL) {
$db = Database::getInstance();
$pdo = $db->getConnection();
$this->query = ‘SELECT userId, username, password, security_level, first_name, last_name, email FROM users WHERE username=:username’;
$this->query_params = [’:username’ => $data[‘username’]];

try {
  $this->stmt = $pdo->prepare($this->query);
  $this->result = $this->stmt->execute($this->query_params);
} catch (Exception $ex) {
  die("Failed to run query: " . $ex->getMessage());
}

$this->stmt->setFetchMode(PDO::FETCH_CLASS, 'Users');
$this->user = $this->stmt->fetch();
if ($this->user) {
  $this->loginStatus = password_verify($data['password'], $this->user->getPassword());
  unset($data['password']); // Unset the password:
  $this->user->clearPassword();
}

if ($this->loginStatus) {
  return $this->user;
}

}

// Function for updating the object:
public function update($data) {

}

// Functin for getting rid of the current object:
public function delete($id = NULL) {
unset($id);
unset($this->user);
unset($_SESSION[‘user’]);
}

}
[/php]

It’s not completely done, for I’m still in the process of developing/design my website:

Finding it difficult to understand frameworks but I think I understand how to structure my code a little better, is it basically separating controllers, views and models into separate files? Like, in your code you have a controller class for product, then a view for search.php, then a repo (which im assuming is model code, right?) for database queries. Is that the sort of thing I should be doing?

Sorry if I’m not getting it yet, I’m very new to frameworks and stuff like that.

Thanks,
Adam

It’s important to remember that you do not have to use OOP or MVC to make a good application or be a good programmer. I find it equally hard to structure code in a sensible way using either method.

What’s most important is that you find a standard and stick to it (at least in that application). And that you remember to always code for others. If you write code thinking you will be the only one managing it you often end up with a terrible spaghetti code nightmare that you might barely understand yourself.

Separating code in some MVC type pattern is very common, and most big frameworks use this today. It is usually done through splitting the code up in separate files that are auto loaded in the application (death over include/require!)

[hr]

When programming in PHP I always recommend you use Composer as a package manager. It will then handle the dependencies you have (updating etc), and it has its own autoloader that is automatically set up to load all the dependencies. It can also very simply be configured to auto load files in your own application.

Autoloading is one benefit of using namespaced classes. Suddenly you can just do this
[php]$post = new Entity\Post;[/php]

And it will automatically load the file in yourdir/Entity/Post.php with no fuss or include/requires needed.

Another benefit is improved readability as you separate concerns.

The biggest benefit I see is dependency injection. The possibility to just swap out the db/cache/router/whatever and change how the application works is great. Also it would be a nightmare to write tests for a code base that is not structured like this.

Unit testing is something many developers should take more seriously. When you get a bigger code base or multiple contributors its essential to be able to run tests to see if code changes broke sonething. If your method then do a lot of db stuff directly it’s hard to properly test the code without setting up a mock database (which would also make it terribly slow).

Thanks for the reply, I guess I’m worrying a bit too much about it all, just wanted to make sure I was on the right track. :slight_smile:

p.s. autoloading is awesome :stuck_out_tongue:

As long as you’re worrying about stuff that will improve your knowledge / code quality then it sounds like it’s a good worry 8)

Sponsor our Newsletter | Privacy Policy | Terms of Service