Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.
I have an application which defines certain actions on common object types.
For example, you can have forum post and images. For each forum post and image you can do the following actions: recommend, comment, rate.
I have currently defined a static class
class CoreObjectUtil
{
protected static $_objObjKey = null;
protected static $_objTypeKey = null;
public static function getComments (...) {...}
public static function getCommentsArray (...) {...}
public static function getRatings (...) {...}
public static function getRatingsArray (...) {...}
}
which is then subclassed like this
class ForumPostUtil extends CoreObjectUtil
{
protected static $_objObjKey = 'forumpost';
protected static $_objTypeKey = 'FP';
}
to provide the relevant functionality for forum posts. The 2 parameters suffice to let the generic code in CoreObjectUtil know what to do for each object type for which these functions are applicable.
To use these functions, I am calling the selectPostProcess() class in my instance classes like this:
public function selectPostProcess ($data)
{
$data = ForumPostUtil::mergeRatings ($data);
$data = ForumPostUtil::mergeComments ($data);
...
}
This works well and keeps the main code centralized in the CoreObjectUtil class with its subclasses providing the data setup to let the code in CoreObjectUtil know what to do.
An alternative approach would be to move the code from CoreObjectUtil into a base instance class which is then inherited in my instance classes. So rather than calling static methods from CoreObjectUtil I would be doing method calls like $this->getComments().
Either approach would work just fine from a functionality type point of view. I'm wondering however what ObjectOriented design guidelines and experienced ObjectOriented developers think of these two approaches. Which way of doing this is preferable and why?
I would appreciate any thoughts/insights on this matter. I can code either way without problem, but I'm having a tough time deciding which route to take.
That code you have now is, I think, the most procedural approach ever posing as OOP i.e what you have now is at the opposite side of OOP. Using the class keyword doesn't make it OOP.
First of all, you should forget about static, it's not that it's bad ot use but it's so easily abused that you really have to try first if the functionality can belong to an object modelling a domain concept (in your case forum related). Only if it doesn't make sense this way, you'll have it as a static method somewhere in a utility class.
Truth be told you have to redesign yur app around the OOP mindset, that is to define classes with behaviour which model a specific concept or process and which have only one responsaiblity. More over you should not mix things like business objects (object which model the forum concepts) with persistence concerns i.e don't put in the same object business functionality and database access. Use a separate class for accessing storage.
Use the Repository pattern to separate business layer from the persistence layer. Try not to mix together create/update functionality with querying IF it complicates things. Use a separate read model specifically for querying in that case.
The code you show us is about querying. You can have a simple DAO/Repository (call it what you want in this case) like this
class ThreadViewData
{
public $Id ;
public $Title;
public $Comments; //etc
}
class ThreadsQueryRepository
{
//we inject the db access object , this helps with testing
function _construct($db) { }
public function GetThread($id){ } //this returns a ThreadViewData
}
The postPRocess functionality is a service that can Merge Ratings and Comments. But maybe the merge functionality is more suitable to the Rating and Comment objects. I don't know the domain to actually give a valid suggestion.
Point is, you have to think in objects not in functions and right now all you have is functions.
I'm working on a PHP MVC but struggling to handle certain aspects of the design, making the User information accessible easily to everything is really puzzling me.
I did have User as an abstract class so you could just use User::getUserId() but I'm not sure that was the right way to do it?
I've now changed it to the following but it's not working.
The Problem
Say I have a basic controller where I want to instantiate the User class every time it's run:
Controller
class controller{
public $user;
function __construct(){
$this->user = new User();
$user->sessionRefresh();
$user->getUserId();
}
}
and then I have a User model class that was instantiated:
User Model Class
class User{
var $userId;
function sessionRefresh(){
if (isset($_SESSION['userId']) && $_SESSION['created'] + 15 * 60 < time())
session_regenerate_id();
}
function getUserId(){
$this->userId = $_SESSION['userId'];
}
}
and then I have more controllers that extend the default controller:
Forum Controller - Very basic just to demo what I'm trying
class forumcontroller extends controller{
function __construct(){
require 'templates/forum.php';
}
}
Forum Template - Very basic example
<html>
<title>Forum</title>
<body>
<div>Welcome User: <?=$user->userId;?></div>
</body>
</html>
Is what I'm doing completely wrong?
I've only recently tried to take the leap from procedural to mvc/oop in PHP and clearly it's confusing me still.
Use $this->user->userId; instead of $user->userId;
You have to echo .... <? echo $user->userId; ?>
Stop editing your code while someone is helping
Thx for Downvote
Yes, what you are doing is completely wrong, when it comes to MVC design pattern. Partially it is related to the fact, that MVC is an advanced pattern. You should not even be trying to use it until you have solid understanding of OOP.
So .. the problems with your code.
When you use include or require, the file which you "embedded" is ins the scope in which it was used. Read about variable scope and template
Model is not a class or an object. It is a layer, which contains multiple structures of different types of structures. This should cover the short version.
Interaction with User instance would be part of domain business logic, which is responsibility of model layer. None of this should be in the presentation layer (which is mostly composed of views, controllers and templates).
View is not a template. In MVC design pattern views are structures which are responsible for most of user interface logic. The decide what sort of response should be returned (wither a HTML composed from multiple templates, some XML or JSON file, maybe just a HTTP header).
The var statement was used in PHP4 to define class variables. In PHP5 we have public, private and protected.
In general, you should not have public variables in class. The break the encapsulation.
Class constructors should not perform computation. It makes the code hard to test.
Update
The bottom line is this: **stay away from MVC* for at least 3 more month. You need to learn a lot more about OOP before you can fully utilize and understand MVC design pattern.
I would recommend the list of materials at the bottom of this post. Start with the first two books, then watch the lectures and then the PoEAA (last one in the list) book.
I am in somewhat a mix of MVC and objects and I was wondering if I am going about it the right way or if i could change something to improve my ways.
I have a webapp that uses an MVC model with objects. I chose to use objects as well so i can load them whereever i need them. One of the objects is a Tag object (which extends the basemodel for DB access). With my MVC framework I can load models from the basecontroller and also form the basemodel (both run their own instance). At first I was loading the Tag object from the controller. Today I decided to load it from the model. Both methods work fine, but what would be the better choice and why?
From the controller (tag_controller.php)
function getTags()
{
$this->_tagobject = $this->load->object('tagobject');
$tags = $this->tags-getSomeTags();
}
From the controller with model.
tagmodel.php
function __construct()
{
$this->_tagobject = $this->load->object('tagobject');
}
function getTags()
{
return $this->_tagobject->getTags();
}
tagcontroller.php
function __construct()
{
$this->_tagmodel = $this->load->model('tagmodel');
}
function getTags()
{
$this->_tagmodel->getTags();
}
Any suggestions for improvement are very welcome, because I can't seem to see a real benefit besides having a flexible object.
For most applications, your interaction with a tag object would follow CRUD operations (Create, Read, Update, Delete) so to me it leans more towards the model.
If I were to use tags in Zend Framework (MVC based PHP framework), I would create a tags class extending the Zend_Db_Table class and also a tag class representing the individual data object so I would be able to use it like:
$tags->insert(new Tag('keyword'));
$tags->find('keyword');
$tags->delete('keyword');
Placing this functionality in a controller wouldn't make sense. Then again, that's all my opinion.
I am trying to learn OOP. The so called 'real world' examples in the books I am reading aren't helping.
All the examples like Pet, Car, Human aren't helping me anymore. I need REAL LIFE examples that like registration, user profile pages, etc.
An example:
$user->userName = $_POST['userName'];//save username
$user->password = $_POST['password'];//save password
$user->saveUser();//insert in database
I've also seen:
$user->user = (array) $_POST;
where :
private $user = array();
Holds all the information in an array.
And within that same class lies
$user->getUser($uid);
// which sets the $this->user array equal to mysqli_fetch_assoc() using
//the user id.
Are there any real world examples implementing OOP in the many different php applications (registration, login, user account, etc)?
OOP is not only about how a single class looks and operates. It's also about how instances of one or more classes work together.
That's why you see so many examples based on "Cars" and "People" because they actually do a really good job of illustrating this principle.
In my opinion, the most important lessons in OOP are encapsulation and polymorphism.
Encapsulation: Coupling data and the logic which acts on that data together in a concise, logical manner
Polymorphism: The ability for one object to look-like and/or behave-like another.
A good real-world example of this would be something like a directory iterator. Where is this directory? Maybe it's a local folder, maybe it's remote like an FTP server. Who knows!
Here's a basic class tree that demonstrates encapsulation:
<?php
interface DirectoryIteratorInterface
{
/**
* #return \Traversable|array
*/
public function listDirs();
}
abstract class AbstractDirectoryIterator implements DirectoryIteratorInterface
{
protected $root;
public function __construct($root)
{
$this->root = $root;
}
}
class LocalDirectoryIterator extends AbstractDirectoryIterator
{
public function listDirs()
{
// logic to get the current directory nodes and return them
}
}
class FtpDirectoryIterator extends AbstractDirectoryIterator
{
public function listDirs()
{
// logic to get the current directory nodes and return them
}
}
Each class/object is responsible for its own method of retrieving a directory listing. The data (variables) are coupled to the logic (class functions i.e, methods) that use the data.
But the story is not over - remember how I said OOP is about how instances of classes work together, and not any single class or object?
Ok, so let's do something with this data - print it to the screen? Sure. But how? HTML? Plain-text? RSS? Let's address that.
<?php
interface DirectoryRendererInterface
{
public function render();
}
abstract class AbstractDirectoryRenderer implements DirectoryRendererInterface
{
protected $iterator;
public function __construct(DirectoryIteratorInterface $iterator)
{
$this->iterator = $iterator;
}
public function render()
{
$dirs = $this->iterator->listDirs();
foreach ($dirs as $dir) {
$this->renderDirectory($dir);
}
}
abstract protected function renderDirectory($directory);
}
class PlainTextDirectoryRenderer extends AbstractDirectoryRenderer
{
protected function renderDirectory($directory)
{
echo $directory, "\n";
}
}
class HtmlDirectoryRenderer extends AbstractDirectoryRenderer
{
protected function renderDirectory($directory)
{
echo $directory, "<br>";
}
}
Ok, now we have a couple class trees for traversing and rendering directory lists. How do we use them?
// Print a remote directory as HTML
$data = new HtmlDirectoryRenderer(
new FtpDirectoryIterator('ftp://example.com/path')
);
$data->render();
// Print a local directory a plain text
$data = new PlainTextDirectoryRenderer(
new LocalDirectoryIterator('/home/pbailey')
);
$data->render();
Now, I know what you're thinking, "But Peter, I don't need these big class trees to do this!" but if you think that then you're missing the point, much like I suspect you have been with the "Car" and "People" examples. Don't focus on the minutiae of the example - instead try to understand what's happening here.
We've created two class trees where one (*DirectoryRenderer) uses the other (*DirectoryIterator) in an expected way - this is often referred to as a contract. An instance of *DirectoryRenderer doesn't care which type of instance of *DirectoryIterator it receives, nor do instances of *DirectoryIterator care about how they're being rendered.
Why? Because we've designed them that way. They just plug into each other and work. This is OOP in action.
Purchase a book like "PHP and Mysql everyday apps for Dummies".
Its old I know [2005] but it shows concepts of User Logins, Forum, Shopping Carts, etc in both Procedural and Object Oriented with Mysqli.
It helped me learn Object Oriented PHP, I studied it a lot. Well worth the money.
OOP is much like grouping bits of your program into reuseable pieces. Its not that hard to be honest with you its just the idea of packing your functions into classes.
Real world mini example of OOP stuff below:
CLASS DATABASE
CLASS SESSIONS
CLASS WEBFORMS
CLASS EMAIL
CLASS ACCOUNTS (Example Functions below)
FUNCTION SELECTACCOUNT
FUNCTION CHECKPASSWORD
FUNCTION CHECKUSERNAME
FUNCTION CREATEACCOUNT
I hope you keep at it, PHP 6 will be re-engineered to support OOP more than ever.
Good Luck!
Whilst I know that this question has been answered already, I feel as though I can add value here.
I don't believe that you should use PHP as a programming language to learn OOP. If you wish to learn OOP for web applications, you should really be looking at C# or Java. Once you have learned OOP, then you can apply this knowledge to PHP. One example of a book I used to learn OOP was Big Java by Cay S. Horstmann
Why do I say this??? Because there are literally millions of examples on PHP of how to do stuff, however not many are examples of how to program properly. Further to this, PHP allows you to take many shortcuts, which would not be acceptable with the likes of Java. As such, if you program PHP with a Java head, then I believe that you will be a stronger programmer. OOP is not language specific, it is a programming paradigm.
If you must learn OOP using PHP, then I would recommend that you take a look at some real source code in public repositories of github. You can search them in packagist.org. If they are a decent public repository, they will contain a readme.md file which would show you how to use the composer packages. e.g https://github.com/moltin/laravel-cart is an example of a shopping cart package which you would be able to use in your application. Notice how you don't need to look at the package source code to understand what the packages do. The package has been written, and you don't care about how they work, but you use them so you only need to know how to use them. This is exactly what OOP is about.
I don't care how the shopping cart class adds an item to the cart, I just want to create a new cart and add something to it.
What you are doing however is diving into the source code as a tool to understand how OOP works.
Further to this, and probably more importantly, for web application development, I would research the MVC design pattern.
The MVC design Pattern stands for Model, View, Controller. Where in the case of a web application, The Model is responsible for modelling the database, the view is responsible for displaying content to the user. The controller is responsible for interacting with the model and handling user input.
I then think you should try to install the Laravel Framework or other "decent modern framework" on your local machine. Why do I say modern, because modern frameworks require a minumum PHP version of 5.3+ which mean that the PHP on your machine would support real OOP similar to that which you would get from the likes of Java.
There are many tutorials which will show you how to build web applications in laravel. Immediately, you will see that when you create a controller, you extend a BaseController. When you create a Model, you extend Eloquent. This means that you will already be using Polymorphism in your code. You will see that by using classes, they are being encapsulated, and you will see that each class has a specific role.
When you would like to interact with the database, you will initially create a new Model object within the controller methods. As you start to learn more, you will start learning how to inject dependencies into the controller, then learning how to dump your models and create repositories and program to interfaces instead.
A decent book on learning Laravel for beginners would be https://leanpub.com/codebright by Dale Rees. I met Dale at a Laravel meetup about 2 weeks ago.
Further to this, as you become more proficient building web applications, you will start to learn how to apply the following principles to your programming:
Single Responsibility Principle
Open Closed Principle
Liskov Substitution Principle
Interface Segragation Principle
Dependency Inversion Principle
As astropanic said, you could take a look at the source code of a good PHP framework or library. I recommend Zend Framework, it's very modular and has a great, professional design. I would say it is a very good piece of object-oriented PHP code.
Still, I think it's not that easy to learn from a huge piece of production code, since it wasn't really made to teach you anything. But if you want real-world object-oriented PHP code, the Zend Framework (or Symfony, or maybe CakePHP) is probably the way to go.
I'd advise you to stay away from any framework at this moment, if you do not know OOP, digging into zend or any other framework would be too much.
PHP OOP is quit funny... like ha ha funny, because it's supported, but PHP is not an OOP language like java or c#.
Short example just to underline my statement:
// define class
class User {
// define properties and methods
public $name = "";
}
// instantiate class
$user = new User; // or new User() or new user, it's all the same
echo $user->name;
but if you want to do OOP "on the fly" you can do the following:
$user = (object) array('name' => 'Peter');
and then
$user->name;
but you can use OOP like you would in java or c# but not to the same extend - and have in mind, popular systems like wordpress and drupal are not pure OOP! but you can do inheritance and other classing OOP stuff in PHP as well.
I haven't gone far in PHP OOP, but the more i get into it the more easier it becomes. The objects examples are just there for you to understand how OOP works. I understand and been through this before, OOP is just about properties and methods ( normal variables and functions). I programed in real OOP myself applying the examples from my tutorials and didn't necessarily have to be in real world. That is just like been spoon fed and you would never understand OOP and would be easy to forget. My advice learn to understand. If you understand, you can do anything and would realize the power of OOP. I downloaded this book and i think you should too. But that is just like someone building your apps for you...
Here a link to the book PHP and Mysql everyday Apps For Dummies
you're right 99% of the tutorials that you'll find online are too basic, and they don't make sense. My suggestion to anybody trying to learn object oriented programming is:
find some code that you've written in procedural programming and try to convert it into OOP.
Take all your variables and make them a properties of a class, find a suitable class name. Then take all your functions, if you have functions, and group them within a class. If you wrote your code without using functions, first try to convert your code into a group of functions. find suitable function names, a function should define what a piece of code does. Once you have your functions move them into a class.
I'll give you a simple example of a pagination code that I converted into a reusable pagination class. find the full tutorial on youtube: https://www.youtube.com/watch?v=X38IRlyY_ww&t=91s , link to the source code is on the description box
class Paginator{
/*the class need to know the current page to calculate the offset
the offset tell my sql how many rows to skip */
private $current_page;
//the limit is the number of results to display per page
private $limit;
/*pagination links to display below the results with next
and previous button*/
private $pagination_links;
//the last page of your pagination links
private $last_page;
.
. etc
public function __contruct($number_of_rows_found,$results_to_display_per_page){
//the paginator uses the number_of_rows_found to determine the last page
$this->last_page = $number_of_rows_found/$results_to_display_per_page;
//get the current page, set it to 1 by default
$this->current_page = isset($_GET['page']) ? ($_GET['page'] : 1;
}
public function generate_pagination_links(){
this method uses the last_page property to generate pagination links
if the last page is 3, this will give you 3 pagination links
for ($page = 1; $page <= $this->last_page; $page++) {
//use
}
}
public function get_offset_and_limit(){
//this method calculates the offset based on the current page
return "LIMIT $this->per_page OFFSET ".($this->page - 1) * $this->per_page;
}
.
.
.etc
}
}
Then to use the pagination class, you create a new instance of it, and pass the
number of results you want to display per page, and the the number of results returned
by your query as parameters. The pagination class will generate pagination links for
you and calculate the offset and limit. That's a good example of a php reusable
class, you can use it in your multiple projects without having to rewrite or change it.
$paginator = New Paginator($rows_found,8);
$pagination_links = $paginator->get_pagination_links();
$offset_and_limit = $paginator->get_offset_and_limit();
//apend the limit and offset to your sql query
$query = $query. ' '.$offset_and_limit;
$connection = getdbconnection();
$stmt = $connection->prepare($query);
$stmt->execute();
$movies = $stmt->fetchAll();
Ofcourse there're more advanced concepts into OOP that are not covered in this example, but this should give you a basic understanding of how classes and objects work
I suggest also to see my wrapper Arrayzy. It's a native PHP arrays easy manipulation library in OOP way.
So if you work with native PHP array functions - you could do the same things in OOP and Arrayzy helps you with it, for example:
// Functional programming:
$array = ['a', 'b', 'c'];
$resultArray = array_merge($array, ['c', 'd']);
and
// Object-oriented programming:
$obj = Arrayzy\MutableArray::create(['a', 'b', 'c']);
$obj->mergeWith(['c', 'd']);
$resultArray = $obj->toArray();
In both cases the result array will be:
Array(
0 => 'a'
1 => 'b'
2 => 'c'
3 => 'c'
4 => 'd'
)
Check how does this mergeWith method (or other) works under the hood.
I think this is a nice example which shows that almost everything functional code you could replace with OOP code like in this library. But with OOP you get much more and you could also check Functional programming vs OOP question to learn more details what's a cons and props of it.
I have my own hand-rolled PHP MVC framework for some projects that I'm working on. When I first created the framework, it was in the context of building an admin CMS. Therefore, there was a very nice one-to-one relationship between model, view, and controller. You have a single row in the DB, which maps to a single model. The controller loads the model and passes it to the view to be rendered (such as into an edit form). Nice, clean, and easy.
However, now that I'm working on the front end of the site, things are getting sticky. A page isn't always a view of a single model. It might be a user directory listing with 20 users (each a User model). Furthermore, there might be metadata about the request, such as pagination (current page, total pages, number of results) and/or a search query.
My question is, what is the cleanest way to pass all this data to the view?
Some options I'm considering:
Have the controller create an array and pass that to the view as a single parameter:
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$data = array()
$data['models'] = $models;
$data['currentPage'] = $current;
$data['totalPages'] = $total;
return $view->render($data);
}
}
class UserView{
public function render($data){
// render the data
}
}
Create properties in the view class and have the controller populate them:
class UserView{
public $models;
public $currentPage;
public $totalPages;
}
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$view = new UserView();
$view->models = $models;
$view->currentPage = $current;
$view->totalPages = $total;
return $view->render();
}
}
Give the view some sort of generic HashMap or Collection object as a container which can hold any arbitrary number and name of data.
class UserView{
public $collection = new Collection(); // works like a Java collection
}
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$view = new UserView();
$view->collection->add($models,'models');
$view->collection->add($currentPage,'currentPage');
return $view->render();
}
}
I know that technically any of the could work, but I'm unsure of the best choice, or if there's a better or more conventional choice that I'm missing.
I'm going to recommend the concept of Fat Models, Skinny Controllers (or, Fat Models Thin Controllers if you prefer...)
In otherwords, your model is too strict - tying your model to represent only something like a RowDataGateway is extremely limiting.
In fact, I think good models hide the fact that you're reading the data from a database at all. Because, in reality, your data could be in text files, or from a web service, or whatever. If you treat your Model like nothing more than a glorified DBAL, you doom yourself to having tightly-coupled code in your controllers that just won't let you break away from the "data only comes from the database" way of thinking.
I've seen both of the first two methods implemented in popular MVC/templating frameworks.
django uses the first method, passing to the view a dictionary of variables which the view uses to fill the template.
smarty uses the second method, creating a Smarty object and assigning values to each the properties in the container.
Your third method seems to essentially be the same as the second, with minor architecture differences.
Really, I guess I haven't said anything that you haven't thought of already. Basically, these are all sounds ideas, so implement whatever you feel you are most comfortable with.
In the one I use, it has automatically has a view property in the controller that you can access methods and properties on the view. All public properties are then accessible within the view view '$this' since the view is rendered in it's own objects context.
In the controller:
$this->view->myVar = 'test';
And in the view:
$this->myVar; // 'test'
The same goes for the layout since the are both separate instances of the same view object:
$this->layout->myVar = 'test';
And then in the layout:
$this->myVar; // 'test'
The framework used to be proprietary, but is about to be released to the general public. I'd be happy to send you some code from it if you think that'd help. Remember, the simplest answer is usually the best answer.