5

I am just posting this question so some of you might be able to point me in the right way. I am slowly warming up to OOP, starting to understand the concept. I want to make a good solid core or foundation to be used as a CMS backend. It will also use MVC. I have been using http://gilbitron.github.com/PIP/ as the MVC- base.

The thing I cant figure out is the following:

Say, on the projectpage in the backend I have 2 sections: htmltext and projects and I should be able to edit them both. The uri would be something like: //domain/backend/projects (the method would be the index and show the 2 sections)

When i click on projects how should it be handled? //domain/backend/projects/projects/ or //domain/backend/projects/list/

One step further, a project will hold some images or a gallery: //domain/backend/projects/edit/5/gallery/2

My question here is, first: would this be a good way to go and even more important how would this be implemented in OOP

the main projects controller:

class projects {

    function index(){
        // view index
    }

    function edit{
        $project = new Project();
        $projectdata = $project->load(5);
    }
}

A single project controller

class project {

    function __construct(){
        $this->projectmodel = $this->loadModel('project_model'); // prepare the model to be used
    }

    function load($id){
        $this->projectmodel->loadproject($id);
    }
}

project model

class project_model extends model { //extends for DB  access and such

    function __construct(){
        // do stuff
    }

    function loadproject($id){
        return $this->db->query("SELECT * FROM projects where id=" . $id . " LIMIT 1");
    }
}

Now my question. If this project has images, where should I load the image class to handle those? Should I load it in the project_model like $this->images = new Images(); and have a function inside the model

function loadimages($id){
    $this->images->load($id);
}

and then images would be something like:

class image extends model { //extends for DB  access and such 

    function __construct(){
    }

    function load($id){
        return $this->db->query("SELECT * FROM project_images where id=" . $id . " LIMIT 1");
    }
}

It seems controllers and models gets mixed up this way. Yet in a logical way a project is a container that holds projectinfo, which could be text, images and maybe video's. How would I go about to set that up logically as well.

MisterM
  • 175
  • 2
  • 8
  • On a side note, it's a common naming convention to have class names' first letter uppercased `class Image extends Model {`. – Madara's Ghost Mar 23 '12 at 21:07

3 Answers3

23

The original question

The first part, about the URLs is something called: Routing or Dispatching. There is quite good article about it in relationship with Symfony 2.x, but the the idea behind it is what matters. Also, you might looks at ways how other frameworks implement it.

As for your original URL examples, galleries will be stored in DB. Won't they? And they will have a unique ID. Which makes this, /backend/projects/edit/5/gallery/2 quite pointless. Instead your URL should look more like:

/backend/gallery/5/edit         // edit gallery with ID 5
/backend/project/3              // view project with ID 3
/backend/galleries/project/4    // list galleries filtered by project with ID 4

The URL should contain only the information you really need.

This also would indicate 3 controllers:

  1. single gallery management
  2. single project management
  3. dealing with lists of galleries

And the example URLs would have pattern similar to this:

/backend(/:controller(/:id|:page)(/:action(/:parameter)))

Where the /backend part is mandatory, but the controller is optional. If controller is found , then id ( or page, when you deal with lists ) and action is optional. If action is found, additional parameter is optional. This structure would let you deal with majority of your routes, if written as a regular expression.

OOP beyond classes

Before you start in on using or writing some sort of PHP framework, you should learn how to write proper object oriented code. And that does not mean "know how to write a class". It means, that you have to actually understand, what is object oriented programming, what principles it is based on, what common mistakes people make and what are the most prevalent misconceptions. Here are few lecture that might help you with it:

This should give you some overview of the subject .. yeah, its a lot. But is suspect that you will prefer videos over books. Otherwise, some reading materials:

You will notice that a lot of materials are language-agnostic. That's because the theory, for class-based object oriented languages, is the same.

P.S.

Be careful with extends keyword in your code. It means "is a". It is OK, if class Oak extends Tree, because all oaks are trees. But if you have class User extends Database, someone might get offended. There is actually an OOP principle which talks about it: Liskov substitution principle .. also there is a very short explanation

tereško
  • 58,060
  • 25
  • 98
  • 150
  • Thanks tereško, very helpfull and insightfull. I will go over the links you've shared as well. I have been reading up on OOP. How to use OOP is something I start to understand, but once you try to make it work for a bigger system you need to understand well what, and why you do it. The part of the route was an aha-moment. Making the gallery controller handle a project. This does mean a dependency, of which i've learned should been avoided when possible, but for now it's perfectly fine. Thanks for the extensive explanation. I will dive into it. – MisterM Mar 25 '12 at 01:30
  • @MisterM , well .. the gallery-controller is not really handling the project. It is just working with selected gallery. Fact that this gallery is associated to a project has only secondary impact on it. – tereško Mar 25 '12 at 21:25
  • Correction: is-a is a relationship between values and classes. The relationship between classes such as sub-classing or sub-typing is called a-kind-of. To wit: `"foo"` `is-a` `String` while `SubType` `a-kind-of` `SuperType`. – Randall Schulz May 22 '14 at 03:02
  • @RandallSchulz in past few days I have learned that one has to keep it as simple as possible. Yo are (or at least profile says so) a native speaker. But it would be reasonable to assume, that ~98% of the user-base here doesn't have English as the first language. Someone can misunderstand "a kind of" as "kinda like", which would completely fail to communicate the idea behind LSP. – tereško May 22 '14 at 04:12
  • @tereško: This isn't really about language familiarity, it's about using technical terminology correctly. Software development is a highly technical field. We (professionals, anyway) do ourselves a disservice by using terminology so loosely, inaccurately and imprecisely. In any event, people come here to educate themselves and that is what my answers and comments aim to do. Facts about terminology are a valid part of that activity. – Randall Schulz May 22 '14 at 14:19
2

If this project has images, where should I load the image class to handle those? Should I load it in the project_model like $this->images = new Images(); and have a function inside the model

Yes. The goal of the model is to encapsulate business logic so that you only need to write the algorithms once, and share them multiple times. What I would do if I were you is to add a getImages() method to the Project model as the images are a direct attribute to a project, this makes it so that each project knows how to retrieve it's own images.

Your code is missing a few key concepts compared to ORMS I have worked with (hydration and peer/table) classes, so I will try to stick with your code. You can retrieve the images through the Project object, then reference the images via the view 1 of 2 ways:

// Controller
$this->project = new Project();
$this->projectImages = $project->getImages(); // Implemenation #2

// View

// Implemenation #1 (reference to object initialized in controller)
<?php foreach ($project->getImages() as $image): ?>
<img src="<?php echo $image['path'] ?>" />
<?php endforeach ?>

// Implemenation #2 (reference to separate variable initialized in controller)
<?php foreach ($projectImages as $image): ?>
<img src="<?php echo $image['path'] ?>" />
<?php endforeach ?>

Above is demonstrative only, but should give you an idea. I would suggest that you take a look at Doctrine. It is a proven and stable ORM which provides much of what you are attempting to write, plus it adds the missing components that I mentioned earlier; hydration, object relations, and Table classes, along with many other features such as nested sets.

Mike Purcell
  • 19,847
  • 10
  • 52
  • 89
  • Thanks Mike, I will dive into ORM. I did write some new code after reading your answer. This code would also overcome the plurar/single issue people run into. I'm not sure though if this is a logical way to do it. I'll post it. – MisterM Mar 24 '12 at 13:28
  • I used to go plural, but have seen how singular is much better. The only time I pluralize any vars is if I have an array, semantically it just sounds better, plus allows you to name a single element as the singular version of the plural: `foreach ($elements as $element) {` – Mike Purcell Mar 24 '12 at 20:01
0

(since the question was long enough by itself I didn't want to extend it so I've added this as an answer)

projects.php this controller extends the project controller. the main purpose is to handle the multiple getAllProjects() using its own model and then pass it on to the project controller getProject()

class Projects extends Project{

public function __construct()
{
    parent::__construct();
    $this->projects_model = new Projects_model();
}       

public function getAllProjects()
{
    $res = $this->projects_model->getAllProjects();     
    if($res) foreach($res as $v) $projects[] = $this->getProject($v);

    return $projects;
}
}

project.php
the project_model does nothing more than getting projectdata and related data like images

class Project{

public function __construct()
{
    $this->project_model = new Project_model;
    $this->images = new Images();
}


public function getProject($data=NULL)
{
    if($data==NULL) $data = $this->project_model->loadProject($data['id']);
    $images = $this->project_model->loadProjectImages($data['id']);

    return array('project_id' => $data, 'project' => $data, 'images' => $images);
}   


public function getProjectByID($id)
{
    $data = $this->project_model->loadProject($id);
    return getProject($data);
}   

}

The upside of this approach, I can use the project class on it own and it will work. I can encapsulate it in projects and it can be used as well.

Is this a good (and the right) approach to do stuff? Or would it be better to put this all together in one controller and one model class?

MisterM
  • 175
  • 2
  • 8