Models in mvc (best practices, PHP) - php

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/

Related

How can I restrict class instances in PHP?

I'm relatively new to PHP (and to programming in general). Keep that in mind.
Here's the basic scenario. I am building a web-based mini-university.
There are two key classes: Lesson and LessonSeries.
Lesson has a Series property, which stores a singular LessonSeries if that lesson belongs to a series. It may not.
LessonSeries has a Lessons property, which will hold an array of Lesson objects..
Both classes have an 'Id' property, which is an integer and will be unique to their class. My database ensures there will not be two Lessons or two LessonSeries with the same Id.
In numerous pages throughout my website, Lesson objects are iterated and (if they have a series), they will display the series they belong to, and sometimes use some of the other LessonSeries methods and properties.
So here's the issue: When a Lesson is pulled from the Db, it constructs a LessonSeries to correspond to it. But I don't want there to exist 20 instances of the same LessonSeries. Certain methods trigger a db query that only needs to be executed once per LessonSeries. This could lead to exponentially more db activity than necessary if there were 20 instances of essentially the same series.
I'm new to programming patterns, but here's what I think I need:
I want a registry of unique LessonSeries.
I want each unique LessonSeries to be shared by all lessons that
belong to it.
Ideally, I want this functionality to be within the LessonSeries class, without having to have a second manager class, if it's at all possible.
Basically, what I want is that, whenever a LessonSeries is constructed, the registry will be checked first for the existence of an the id. If so, what is returned is a reference to the existing LessonSeries. If it DOESN'T exist in the registry, then it is created as usual.
With this said, I have no idea how to make this happen in PHP.
Edit:
It was pointed out in the comments that I need to demonstrate I've attempted to solve the problem for this to be a good question, of sorts, on SO. But that's exactly my problem.
I considered doing something like this:
class LessonSeries {
private static $registry;
public $Id;
public $SeriesName;
public $ImagePath;
public $Description;
private $index;
private $lessons;
private $lessonCount;
public function __construct($seriesName, $imagePath=null, $Id=null, $description = null) {
if(isset(self::$registry[$id])){
return self::$registry[$Id];
}else{
$this->SeriesName = $seriesName;
$this->ImagePath = $imagePath;
$this->Id = $Id;
$this->Description = $description;
self::$registry[$Id] = $this;
}
}
However, I don't think this is how php constructors work. I think I know how to do what I want in Python (using subclasses and such), but PHP doesn't have subclasses. And that's where I'm struggling.
Here's what you can do.
Create a factory object. Move all creation/caching logic there.
Pass that factory to newly created lessons and series so that they could delegate creation to it.
In your page code, strat by creating a factory.
Try to avoid statics if you can. For instance, your code will be easier to unit test if it's real objects.
Here's a pseudo-code demonstrating it:
class LessonsFactory {
private $lessons = [];
private $series = [];
public getLesson($id) {
if (isset($this->lessons[$id])) {
return $this->lessons[$id];
}
$lesson = $this->loadLesson($id);
$this->lessons[$id] = $lesson;
$this->getSeries($lesson[$id]);
}
private loadLesson($id) {
$data = ... // load lesson from db
return new Lesson($data, $this);
}
public getSeries($id) {
if (isset($this->series[$id])) {
return $this->series[$id];
}
$series = $this->loadSeries($id);
$this->series[$id] = $series;
}
private loadSeries($id) {
$data = ... // load series from db
return new LessonSeries($data, $this);
}
}
class Lesson {
private $factory;
public function __construct($data, LessonFactory $factory) {
$this->factory = $factory;
// + code to intialize object with $data
}
// this is how you get series from lessons
public function getSeries() {
return $this->factory->getSeries($this->seriesId);
}
}
// somewhere in your page controller code
$factory = new LessonsFactory();
$lesson = $factory->getLesson($_GET['lessond_id']);

Object oriented use of properties/methods in real webapplications?

I just moved from my "everything-in-functions" coding style to OOP in PHP. I know, there are a lot of sources in order to code properly, but my main problem is, after reading so many tutorials about coding a blog/cms/any web app in OOP it's quite not that understandable to me to code it the right way.
I was told the concept of OOP is basically close to the real world.
So properties are just properties of any object and methods are like what the object should do. But I can't really understand how to use these properties and methods in a web app. In the real world, it's easier than in a web application for me, for instance:
Let's say we want to create a human named "Paul" and he should drive a car.
class human
{
public $gender;
public $name;
function __construct($inGender=null, $inName=null) {
$this->gender = $inGender;
$this->name = $inName;
}
public function driveCar() {
//Let the car be driven.
//...
}
}
$paul = new human('male','Paul');
$paul->driveCar();
Alright, but when it comes to an object in a web application, let's say I want to code a blog system specifically let's focus the posts. Properties could be id, name, content, author of a post, BUT what does a post actually do? It can't really perform an action so I would code it like this:
class post
{
public $id;
public $title;
public $content;
public $tags;
//Overloading the Variables
function __construct($inId=null, $inTitle=null, $inContent=null, $inTags=null) {
if(!empty($inId) || $inId == 0) {
$this->id = $inId;
}
if(!empty($inTitle)) {
$this->title = $inTitle;
}
if(!empty($inContent)) {
$this->content = $inContent;
}
if(!empty($inTags)) {
$this->tags = explode(' ', $inTags);
}
}
}
//In order to pass multiple posts to an constructor I would use a foreach loop
$posts[] = new post(0,'Hello World', 'This is a test content','test blog oop');
$posts[] = new post(1,'Hella World!', 'This is made my myself','test test test');
//Usage in a template
<?php foreach($posts as $post): ?>
<h1><?= $post->title;?></h1>
<p><?= $post->content;?></p>
<?php endforeach; ?>
The problem here is the method does not do anything, what is not supposed to do I guess. And another problem would be, when I would store data into an array, I can't use a single array with data of posts as several parameters for the constructor. Even if this was a single post, how to accomplish the passing of data as an array properly in order to use the array as the parameters? Maybe I just coded it not the best way, but what would be a better way? I heard of setter and getter and magic methods. What would here best practise?
Alright, but another question. How would I program the create/delete posts functionality, new classes or new methods? I can't really imagine anything of this in the real world concept.
This all is not that clear for me since it's nowhere explained on any tutorial.
Help would be amazing.
regards
answering all your questions needs a book as understanding the oop concept completely needs much reading and practicing.
But to answer your post class I give you an easy to use example where you can create a class which will help you create, read, delete and update posts.
This class can be used in all your projects as it's a class and classes are simply there to make our life easier.
So here we go:
first we create the class:
class posts
{
//we just need a post title and a post content for the new post.
//So we create two properties for post_title and post_content
private $post_title; //the variables are private so we we restrict access the them from outside the class.
private $post_content;
private $connection;
//you really don't need here the _constructor function, but it's your choice.
//For example you can do all your database connection stuff in the constructor method to make it easy for users.
//So if someone creates a posts object it is obvious that he's going to make some interactions with the database so you open connections in your constructor.
//next we create the methods for our posts class
public function __constructor()
{
//here you created the connection to your database. You can retrieve
//the parameters to the mysqli_connect from a eg. config.php file to make your class more dynamic.
$this->connection = mysqli_connect('hostname', 'username', 'password', 'databasename');
}
public function create_post($title, $content)
{
$this->post_title=$title;
$this->post_content=$content;
$query="INSERT INTO posttable(id,title,content) VALUES('$this->post_title','$this->post_content')";
$result=$mysqli_query($this->connection,$query);
}
public function update_post($post_id, $post_title, $post_content)
{
}
public function delete_post($post_id)
{
}
public function view_posts($query, $fields)
{
$result=mysqli_query($this->connection, $query);
$finalData=array();
while ($row=mysqli_fetch_assoc($data)) {
foreach ($fields as $field) {
array_push($finalData, $row[$field]);
}
}
return $finalData;
}
}
//now we're going to use the class.
$post=new posts(); //a new posts object is created and the __construct method opened connection to db.
$post->create_post('some title', 'some content');//inserted new post into database.
$post->update_post($id, $content, $title);//you've just updated a post
$post->delete_post($id);//you've just deleted a post.
//getting posts
$query="SELECT * FROM posts";
$fields=array('id','title','content');
$all_posts=$post->view_posts($query, $fields);
//now you have $all_posts which is filled with an array of all posts from the posts table.
//now you can loop through it and show it on your page.
for ($i=0; $i<=count($all_posts); $i++) {
echo '<h1>'.$mydata[$i].'</h1>';
$i++;
echo '<h2>'.$mydata[$i].'</h2>';
$i++;
echo '<h3>'.$mydata[$i].'</h3>';
}
I didn't implement the update and delete method code but I think it's easy for you to write it by yourself.All my goal was to show you how you can use oop in your web applications. I personally have nearly 50 classes written by myself and do all the major jobs for nearly all possible web application tasks. It's up to you how you create your classes. Just for your knowledge, oop programming needs training and experience. You can do a task in multiple forms and with different solutions. You can find dozens of books which teach you oop design who's concept and goal is to teach you how to design a class to the best way.
I hope I was helpful

Implementing a S.O.L.I.D Domain Object Model in the following project

I have the following example in which I tend to use a couple of classes, to create a simple web app.
The file hierarchy seems like this.
> cupid
- libs
- request
- router
- database
- view
- bootstrap.php
- index.php
The index.php just calls the bootstrap.php which in turn contains something like this:
// bootstrap.php
namespace cupid
use request, router, database, view;
spl_autoload_register(function($class){ /* autoload */ });
$request = new view;
$response = new response;
$router = new router;
$database = new database;
$router->get('/blog/{id}', function($id) use ($database, $view) {
$article = $database->select("SELECT blog, content FROM foo WHERE id = ?",[$id]);
$view->layout('blogPage', ['article'=>$article]);
});
As you can probably tell, my problem is this line:
$article = $database->select("SELECT blog, content FROM foo WHERE id = ?", [$id]);
Which I don't want to use, and instead try a " Domain Object Model " approach.
Now, given that I will add another folder called domain, with blog.php
> cupid
- domain
- Blog.php
- libs
...
And fill blog.php with properties mapping table rows, and getter and setters ..
namespace App\Domain;
class Blog {
private $id, $title, $content, $author;
public function getTitle(){
return $this->title;
}
public function setTitle($title){
$this->title = $title;
}
...
}
My question is: Assuming my understanding of DOM is so far correct, and that I have a CRUD/ORM class, or a PDO wrapper to query the database;
"How can I tie together, i.e. the blog model with the PDO wrapper to fetch a blog inside my bootstrap file?"..
As far as a Domain Object you basically already have written one, your blog object. To qualify as a domain model all a class must to is to provide a representation along with any of the functionality of a concept within your problem space.
The more interesting problem here and the one you appear to be struggling with is how to persist a domain model. Keeping with the tenet of the single responsibility principle your Blog class should deal with being a blog post and doing the things that a blog post can do, not storing one. For that you would introduce the concept of a repository of blog posts that would deal with storing and retrieving objects of this type. Below is a simple implementation of how this can be done.
class BlogRepository {
public function __construct(\cupid\database $db){
$this->db = $db;
}
public function findById($id){
$blogData = $this->db->select("select * from blog where id = ?", [$id]);
if ($blogData){
return $this->createBlogFromArray($blogData);
}
return null;
}
public function findAllByTag($tag){...}
public function save(Blog $blog) {...}
private function createBlogFromArray(array $array){
$blog = new Blog();
$blog->setId($blogData["id"]);
$blog->setTitle($blogData["title"]);
$blog->setContent($blogData["content"]);
$blog->setAuthor($blogData["author"]);
return $blog;
}
}
Then your controller should look something like this.
$router->get('/blog/{id}', function($id) use ($blogRepository, $view) {
$article = $blogRepository->findById($id);
if ($article) {
$view->layout('blogPage', ['article'=>$article]);
} else {
$view->setError("404");
}
});
To truly be SOLID the above class should be a database specific implementation of a BlogRepository interface to adhere to IoC. A factory should also probably be supplied to BlogRepository to actually create the blog objects from data retrieved from the store.
In my opinion one of the great benefits of doing this is you have a single place where you can implement and maintain all of your blog related interactions with the database.
Other Advantages to this method
Implementing caching for your domain objects would be trivial
Switching to a different data source (from flat files, blogger api, Document Database Server,PostgresSQL etc.) could be done easily.
You can alternatively use a type aware ORM for a more general solution to this same problem. Basically this Repository class is nothing more than a ORM for a single class.
The important thing here is that you are not talking directly to the database and leaving sql scattered throughout your code. This creates a maintenance nightmare and couples your code to the schema of your database.
Personally I always tend to stick the database operations in a database class which does all the heavy lifting of initialising the class, opening the connection etc. It also has generic query-wrappers to which I pass the SQL-statements which contains the normal placeholders for the bound variables, plus an array of the variables to be bound (or the variable number of parameters approach if thats suits you better). If you want to bind each param individually and not use the $stmt->execute(array()); You just pass in the types with the value in a data structure of your choosing, multi dim array, dictionary, JSON, whatever suits your needs and you find easy to work with.
The model class it self (Blog in your case) then subclasses the Database. Then you have a few choices to make. Do you want to use the constructor to create only new objects? Do you want it to only load based on IDs? Or a mix of both? Something like:
function __construct(id = null, title = null, ingress = null, body = null) {
if(id){
$row = $this->getRow("SELECT * FROM blog WHERE id = :id",id); // Get a single row from the result
$this->title = $row->title;
$this->ingress = $row->ingress;
$this->body = $row->body;
... etc
} else if(!empty(title,ingress,body)){
$this->title = title;
... etc
}
}
Maybe neither? You can skip the constructor and use the new(title, ingress, body), save() and a load(id) methods if thats your preference.
Of course, the query part can be generalised even further if you just configure some class members and let the Database-superclass do the query building based on what you send in or set as member-variables. For example:
class Database {
$columns = []; // Array for storing the column names, could also be a dictionary that also stores the values
$idcolumn = "id"; // Generic id column name typically used, can be overridden in subclass
...
// Function for loading the object in a generic way based on configured data
function load($id){
if(!$this->db) $this->connect(); // Make sure we are connected
$query = "SELECT "; // Init the query to base string
foreach($this->columns as $column){
if($query !== "SELECT ") $query .= ", "; // See if we need comma before column name
$query .= $column; // Add column name to query
}
$query .= " FROM " . $this->tablename . " WHERE " . $this->idcolumn . " = :" . $this->idcolumn . ";";
$arg = ["col"=>$this->idcolumn,"value"=>$id,"type"=>PDO::PARAM_INT];
$row = $this->getRow($query,[$arg]); // Do the query and get the row pass in the type of the variable along with the variable, in this case an integer based ID
foreach($row as $column => $value){
$this->$column = $value; // Assign the values from $row to $this
}
}
...
function getRow($query,$args){
$statement = $this->query($query,$args); // Use the main generic query to return the result as a PDOStatement
$result = $statement->fetch(); // Get the first row
return $result;
}
...
function query($query,$args){
...
$stmt = $this->db->prepare($query);
foreach($args as $arg){
$stmt->bindParam(":".$arg["col"],$arg["value"],$arg["type"]);
}
$stmt->execute();
return $stmt;
}
...
}
Now as you see the load($id), getrow($query,$args) and query($query,$args) is completely generic. ´getrow()´is just a wrapper on query() that gets the first row, you may want to have several different wrappers that to or interpret your statement result in different ways. You may also even want to add object specific wrappers to your models if they cannot be made generic. Now the model, in your case Blog could look like:
class Blog extends Database {
$title;
$ingress;
$body;
...
function __construct($id = null){
$this->columns = ["title","ingress","body","id",...];
$this->idcolumn = "articleid"; // override parent id name
...
if($id) $this->load($id);
}
...
}
Use it as so: $blog = new Blog(123); to load a specific blog, or $blog = new Blog(); $blog->title = "title"; ... $blog->save(); if you want a new.
"How can I tie together, i.e. the blog model with the PDO wrapper to fetch a blog inside my bootstrap file?"..
To tie the two together, you could use an object-relational mapper (ORM). ORM libraries are built just for glueing your PHP classes to database rows. There are a couple of ORM libraries for PHP around. Also, most ORMs have a built in database abstraction layer, which means that you can simply switch the database vendor without any hassle.
Considerations when using an ORM:
While introducing a ORM also introduces some bloat (and some learning), it may not be worthwhile investing the time for simply a single Blog object. Although, if your blog entries also have an author, one or multiple categories and/or associated files, an ORM may soon help you reading/writing the database. Judging from your posted code, an ORM will pay off when extending the application in the future.
Update: Example using Doctrine 2
You may have a look at the querying section of the official Doctrine documentation to see the different options you have for read access. Reconsider the example you gave:
// current implementation
$article = $database->select("SELECT blog, content FROM foo WHERE id = ?",[$id]);
// possible implementation using Doctrine
$article = $em->getRepository(Blog::class)->find($id);
However, ideally you define your own repository to separate your business logic from Doctrines API like the following example illustrates:
use Doctrine\ORM\EntityRepository;
interface BlogRepositoryInterface {
public function findById($id);
public function findByAuthor($author);
}
class BlogRepsitory implements BlogRepositoryInterface {
/** #var EntityRepository */
private $repo;
public function __construct(EntityRepository $repo) {
$this->repo = $repo;
}
public function findById($id) {
return $this->repo->find($id);
}
public function findByAuthor($author) {
return $this->repo->findBy(['author' => $author]);
}
}
I hope the example illustrates how easily you can separate your business domain models and logic from the underlying library and how powerful ORMs can come into play.

Am I writing procedural code with objects or OOP?

So basically I'm making a leap from procedural coding to OOP.
I'm trying to implement the principles of OOP but I have a nagging feeling I'm actually just writing procedural style with Objects.
So say I have a list of pipes/chairs/printers/whatever, they are all all listed as products in my single table database. I need to build a webapp that displays the whole list and items depending on their type, emphasis is on 'correct' use of OOP and its paradigm.
Is there anything wrong about just doing it like:
CLass Show
{
public function showALL(){
$prep = "SELECT * FROM myProducts";
$q = $this->db-> prepare($prep);
$q->execute();
while ($row = $q->fetch())
{
echo "bla bla bla some arranged display".$row['something']
}
}
and then simply
$sth = new show();
$sth->showAll();
I would also implement more specific display methods like:
showSpecificProduct($id)->($id would be passed trough $_GET when user say clicks on one of the links and we would have seperate product.php file that would basically just contain
include('show.class.php');
$sth = new show();
$sth->showSpecificProduct($id);
showSpecificProduct() would be doing both select query and outputing html for display.
So to cut it short, am I going about it allright or I'm just doing procedural coding with classes and objects. Also any ideas/hints etc. on resolving it if I'm doing it wrong?
As well as the model practices described by #Phil and #Drew, I would urge you to separate your business, data and view layers.
I've included a very simple version which will need to be expanded upon in your implementation, but the idea is to keep your Db selects separate from your output and almost "joining" the two together in the controller.
class ProductController
{
public $view;
public function __construct() {
$this->view = new View;
}
public function indexAction() {
$model = new DbProductRepository;
$products = $model->fetchAll();
$this->view->products = $products;
$this->view->render('index', 'product');
}
}
class View
{
protected $_variables = array();
public function __get($name) {
return isset($this->_variables['get']) ? $this->_variables['get'] : null;
}
public function __set($name, $value) {
$this->_variables[$name] = $value;
}
public function render($action, $controller) {
require_once '/path/to/views/' . $controller . '/' . $action . '.php';
}
}
// in /path/to/views/product/index.php
foreach ($this->products as $product) {
echo "Product ID {$product['id']} - {$product['name']} - {$product['cost']}<br />\n";
}
A better fit would be to implement a repository pattern. An example interface might be
interface ProductRepository
{
public function find($id);
public function fetchAll();
}
You would then create a concrete implementation of this interface
class DbProductRepository implements ProductRepsoitory
{
private $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function find($id)
{
// prepare execute SQL statement
// Fetch result
// return result
}
public function fetchAll()
{
// etc
}
}
It's generally a bad idea to echo directly from a method or function. Have your methods return the appropriate objects / arrays / whatever and consume those results.
The scenario you are describing above seems like a good candidate for MVC.
In your case, I would create a class strictly for accessing the data (doing selects of product categories or specific products) and then have a different file (your view) take the output and display it.
It could look something like this:
class Product_Model {
public function find($prodId) { ... }
public function fetchAll($category = '') { ... }
public function search($string) { ... }
}
Then somewhere else you can do:
$products = new Product_Model();
$list = $products->fetchAll(37); // get all from category 37
// in true MVC, you would have a view that you would assign the list to
// $view->list = $list;
foreach($ilst as $product) {
echo "Product ID {$product['id']} - {$product['name']} - {$product['cost']}<br />\n";
}
The basic principle of MVC is that you have model classes that are simply objects representing data from some data source (e.g. database). You might have a mapper that maps data from the database to and from your data objects. The controller would then fetch the data from your model classes, and send the information to the view, where the actual presentation is handled. Having view logic (html/javascript) in controllers is not desirable, and interacting directly with your data from the controller is the same.
first, you will want to look into class autoloading. This way you do not have to include each class you use, you just use it and the autoloader will find the right file to include for you.
http://php.net/manual/en/language.oop5.autoload.php
each class should have a single responsibility. you wouldn't have a single class that connects to the database, and changes some user data. instead you would have a database class that you would pass into the user class, and the user class would use the database class to access the database. each function should also have a single responsibility. you should never have an urge to put an "and" in a function name.
You wouldn't want one object to be aware of the properties of another object. this would cause making changes in one class to force you to make changes in another and it eventually gets difficult to make changes. properties should be for internal use by the object.
before you start writing a class, you should first think about how you would want to be able to use it (see test driven development). How would you want the code to look while using it?
$user = new User($db_object);
$user->load($id);
$user->setName($new_name);
$user->save();
Now that you know how you want to be able to use it, it's much easier to code it the right way.
research agile principles when you get a chance.
One rule of thumb is that class names should usually be nouns, because OOP is about having software objects that correspond to real conceptual objects. Class member functions are usually the verbs, that is, the actions you can do with an object.
In your example, show is a strange class name. A more typical way to do it would be to have a class called something like ProductViewer with a member function called show() or list(). Also, you could use subclasses as a way to get specialized capabilities such as custom views for particular product types.

Is this the correct way to do this PHP class?

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.

Categories