MVC Refactor, Take 2

Interesting, I think we need a better set of examples to properly see how the different approaches work in a real application.

I’m going to suggest we implement a basic blog using each of the different approaches.

Project specification:

  1. A password protected admin area with an admin naviation, header/footer
  2. In the admin area you can view a list of blogs and add a new one or edit or delete an existing one
  3. Each blog has a Title, URL slug, Content and Date
  4. A publicly visible site that has a different header/footer to the admin which
  • Lists all the blog titles and dates on the home page
  • Clicking a blog title shows a different page containing blog title/date and content on a url such as /blog/{url-slug}

For simplicity let’s use this horribly insecure but basic authentication class, as authentication itself doesn’t matter for this example


class Authentication {
	private $password = 'password';

	public function __construct() {
               session_start();
	}
	public function logIn($password) {
		if ($password == $this->password) {
			$_SESSION['loggedIn'] = true;
		}
	}

	public function isLoggedIn() {
		return isset($_SESSION['loggedIn']) && $_SESSION['loggedIn'] == true;
	}
}

This should be simple enough to build in a reasonable amount of time and complex enough to properly demonstrate a working system. It might also be worth using a standardised Blog domain model with this API:

class Blogs {
	public function save($url, $title, \DateTime $date, $content);
	public function load($url);
}

Ok. I accept the challenge. It might take a little longer for me, than you might expect, as I work a normal job (and it has nothing directly to do with PHP programming.) So be patient with me. :blush:

Scott

I have another question. Do you really have to assign variables for the protected functions before your constructor or is this an option? I know that it’s suppose to do something, but I don’t understand how the logic of it works.

Ok. I’ve reworked the parameter stuff by moving it to the request, added a configuration class, which is the URL mapping for the application parameterization (I am still not sure what to call this). I also just started with the blog code. The framework now looks and runs like this.

Scott

quick question can I clone it and edit composer so I can add blade ?

It’s open source with an MIT license. You can do what you’d like with it. :smile: Just know that it is only an experiment currently.

Edit: but it would also be nice, if you could just create a branch and a PR for it.

Edit2: One more note. I am still figuring out how to call secondary objects through included templates. Normally, you’d have another controller to do this in a typical framework (which adds to the craziness of controllers). However, since we are “calling” all the models needed from View, we should have some automation to do it. In the end, I would like to parse the templates to see what objects are called on and store them in some meta data, so that the Model can be built quickly and efficiently. For now though, I am just calling the objects by name through the included template.

Oh, and I realize, my “including” of templates isn’t quite what it should be either. We’d actually need to have blocks like in Twig and I assume Blade would have, so we can include, say, a base template, but where the main object code would be inserted, in the “content” block.

Scott

Thanks for the reply I would also like to ask if you don’t mind that I will put it on c9,io.

Sure. I’d only ask you to somehow reference back to the original repo in some way.

Edit: Nevermind. I thought C9 was something else.

Scott

@TomB - I am finished with the authentication bit. How would you suggest I create, update and delete the blogs? Using a simple file with JSON stored in it? I mean, what should I use to persist the blog articles?

Scott

Maybe SQLite via PDO? That way you’re still writing SQL, but the data is stored in an ordinary local file.

Ok. Thanks Jeff. That is what I’ll do.

Scott

Just a small update. I’ve gotten most of this done. However, I keep finding myself fiddling with new “features” and the last one, adding a subtemplate, just blew a hole in my logic for some reason and I am having fun figuring out what I screwed up.

One thing I found while doing this exercise is, instead of controllers I am now having to put in logic for “page” decisions in the view. For instance, to show the page to create a blog is a GET. But, it isn’t a GET for the blog entries (which is the default page) - it is a GET for the blog entry form. So, I have to decipher between a “normal” GET for the blog entries or a GET for the blog entry form (the work that a router with a controller does). The two GET pages both use the same object “blog” in the end, and the core to which View to render in webMVc and this two different view needs is the cause for the logic issue about which part of the “blogAdmin” template to show.

Currently I am handling it over the /slug parameter. In other words, the slug can also be a predefined function in the template, like “/createBlog”. It works. That is all I can say about it currently. :smile: LOL!

Scott

So, my attempt at the thinnest ever controller in an MVC framework (if you can call it a framework) is working.

https://github.com/SkooppaOS/webMVc

It isn’t elegant. It isn’t proper in a lot of places, I know. I am probably making a ton of beginner’s mistakes too. So go easy on me, but do let me know where I did things incorrectly. It was a great learning experience. I now also know, looking at code and understanding it and actually writing code properly is two completely different challenges. LOL! :smiley:

I can’t believe how long it took me to fix what ended up a couple of simple mistakes, like the wrong order of method calls in the request messing up authentication or having to know you need to set a session variable before the header is sent. Things I didn’t really need to care about before…

At any rate. It works and thanks for the challenge. I can also now appreciate the usefulness of the controller in the “normal” frameworks. Still, the logic of just creating the necessary models and views seems simpler, if it weren’t for the difficulty of matching intents to actual actions, which is what the controller is actually for.

Scott

Nice to read that you found it helpful for yourself.
it is a good thing for any developer to make a cms/mvc once so they try to get their head around it.

I wish you a nice day!

Another small update.

Because the troubleshooting went so poorly, I decided to look into using Xdebug with PHPStorm and got it working (it was actually quite easy) and started playing with break points and the like. This makes inserting val_dumps and echo’s and print_rs (and having to clean them up again, without breaking anything) totally obsolete. How cool is that? :smile:

Scott

1 Like

I just ran into this short article.

No service container in the controller! Is this another way of saying, your controllers should be really thin? So thin, you shouldn’t be calling the DI container?

Also, was my code so bad, it isn’t worth commenting on? I did create the world’s thinnest controller. LOL!

Scott

No, I don’t think so. It’s the difference between…

// Dependency injection

class HelloController
{
    private $templating;
    private $router;
    private $orm;
    // etc

    public function __construct($templating, $router, $orm /* etc */)
    {
        $this->templating = $templating;
        $this->router = $router;
        $this->orm = $orm;
        // etc
    }

    public function indexAction()
    {
        // render a template
        $this->templating->render('Hello_index.html.php');

        // generate a URL
        $this->router->generate('hello_index');

        // query persisted object
        $this->orm->findById(1);
    }
}

and…

// Service container

class HelloController
{
    private $container;

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

    public function indexAction()
    {
        // render a template
        $this->container->get('templating')->render('Hello_index.html.php');

        // generate a URL
        $this->container->get('router')->generate('hello_index');

        // query persisted object
        $this->container->get('orm')->findById(1);
    }
}

or even…

// Service container via base class

class HelloController extends BaseController
{
    public function indexAction()
    {
        // render a template
        $this->render('Hello_index.html.php');

        // generate a URL
        $this->generateUrl('hello_index');

        // query persisted object
        $this->getOrm()->findById(1);
    }
}

The DI example is the most decoupled; the container via base class is the most convenient. In most situations, I would say follow the DI example, because decoupling is important a lot of the time. But controllers may be a special case. If we manage to keep our controllers decoupled from the framework, then – in theory – we could re-use the same controller code without change across several frameworks. It would be an interesting academic exercise to do so across frameworks such as Symfony and Laravel. But in the real world, changing frameworks doesn’t happen often, so investing time and code to ease that scenario isn’t likely to pay off. Plus, there’s still enough differences between frameworks that it’s often easier just to rewrite the controller for a given framework.

My biggest concern, which I mentioned last time, is the baked-in relationship between URLs and classes. Controller and model classes are automatically loaded and executed based on the first path segment. Certainly automatic decisions such as those would help thin the code we’d have to write, but it’s also so inflexible that, for me, it would be a non-starter.

Gotcha on the DI container. That makes sense.

Would you agree, when I say, a core object is always the target for a web page or REST api call? Like /blog, /user, /thread, /category, etc., especially when needing to manipulate that object (i.e. update, create, delete)? Would you also agree that templates always build off of the core object for a rendered web page first, anything else on the page is like a secondary object?

Scott

I agree with this in principle but as long as you adopt a convention-over-configuration approach where this behaviour can be changed where required, it’s just as flexible and requires a lot less work when you’re using the default approach.