Models, middleware & philosophy / architecture

This is a very general question but since the Slim community seems to have a high standard for code organization and has given me some of the best coding advice around I’d figure this was worth asking. :slight_smile:

As my application is growing I’m a little unclear about what is the role of some Classes in my application and how I should organize things. For example my application directory is set up like this:

src/
     middleware/
        Auth.php
     dependencies.php
     settings.php 
     routes.php 

My Auth.php class is obviously middleware because it needs to be executed during each route (checking for logged in status, etc…

Is every class I create “middleware” and therefor belongs in the middleware directory? In the past I thought of every Class that worked with the database as a “Model” are they all just considered “middleware” now?

How does one call the complex logic that may occur within a class?

Would you call individual methods from the route?

Should all of my classes be located in the container?

(I know there’s many ways to do this but, I respect the design principles that most members here have)

Let’s look at an example… Say I have a route that needs to:

  1. Handle an image upload
  2. Verify the image
  3. Resize the image and save
  4. Resize the image and save again

Would it be wise to call each method from inside the route:

$app->post('/image', function ($request, $response, $args) {
	$img = new Img();
	if($img->upload()){
		if($img->verify(){
			if($img->resize(400)){
				if($img->resize(200)){

				}
			}
		}
	}
});

or should I let the class handle the logic and method calling:

$app->post('/image', function ($request, $response, $args) {
	$img = new Img();
	$img->do_all_img_stuff();
});

Thanks!

I’ll take a stab at some of these questions. You may want to have a look at PHP-PDS/skeleton, although most of your questions are about what goes into the src directory. In my case I call it app, but that’s just a preference.

In my app (or src) directory, I keep directories for Controllers, Middleware, Models, Providers, Traits, and Views. But again, that is just my preference. In your example, when hitting that route I’d call a Controller, perhaps called ImageUploadController, and call a create() method on the controller. Traits and Views are probably obvious in their use, and I use Providers for classes that hook into external services like other APIs, sending email, etc. Those then get used by my controllers where appropriate.

Have a look at the amazing @akrabat’s Slim Bookshelf example app too.

$app->post('/image', 'Controllers\ImageUploadController:create')->add('App\Middleware\AuthMiddleware');

The create method on the controller might then take the image and call various methods on some sort of an Image model (perhaps stored at /src/Models/Image.php) to validate() then resize(1024, 766), then save(). Finally the controller might look for a View stored at /views/image/upload.html.twig, render the view, and return the response.

Your logic above starting at $img = would therefore partially live within the controller. The controller methods should be fairly simple and call methods on your model, more like the two $img lines within your last code example. Maybe something like this.

public function create(Request $request, Response, $response, $image) {
    $img = new Image();

    if (! $img->upload()) {
        // return view with error
    }

    if (!$img->verify() {
        // return view with different error
    }

    $img->resize(200);
    $img->save();

    return $this->view->render($response, 'views/image/upload-success.html.twig');
}

Of course don’t expect any of that code to work. :slight_smile:

While based on Laravel, you might find this video series well worth watching and I believe it is free. I subscribe to Laracasts even though I don’t use Laravel (though I do use Eloquent). The tutorials are amazing. Since you’re on your own to provide Slim with what Laravel provides for itself, you can apply many of the principles Laravel uses to build up your own Slim app.

Helpful as always Tim! I’ve been hooked on notorm (http://www.notorm.com/) ,but it was the first and only db/abstraction layer I’ve ever worked with… wowee it’s easy to write and look at. It’s dead simple! BUT the documentation is limited and I have no idea how to get error messages from notorm.

How does elequent compare to notorm?

Some people love Eloquent, some really dislike it. I think for many people the first (or only) ORM they learn is the one they like. For me it was easy to learn, well documented, and tons of people writing tutorials on it. I don’t have any experience with notORM and hadn’t heard of it before.

hmmm.

Actually not to sound stupid, but I’m unclear about them. I have no idea what a “trait” is and when i think “views” I’m thinking an html template (i.e.: twig, etc)

In your fantastic example (https://github.com/tflight/auth-example) *I was thinking of your phtml files in the “template” directory as views, but maybe I’m getting all this mixed up!

In your code example above when you:

return $this->view->render($response, ‘views/image/upload-success.html.twig’);

Is that basically one of your templates? So in your github example that would be stored in appRoot/templates ? But in this discussion you store these views in appRoot/src/Views ? Same thing i guess?

And do you have a modelController for everything just to keep consistency? So even if you’re grabbing all rows from a table which might be a couple of simple lines in total you’ll still create a modelController which then contacts the Model which in turn does the query?

btw…my coding never looked so clean… thanks again!

Yes, the terms ‘views’ and ‘templates’ are often used interchangeably. People trying to adhere to ‘MVC’ patterns of code separation would typically call them ‘views’. But few people will be confused if you call them templates. For perhaps no good reason at all, if I’m just using PHP code to render output then I often call them templates but if I’m using something like Twig or Blade then I typically call them views.

In my views directory I have have “master” views which then other views “extend”. (I’m using Twig most of the time.) I keep directories under views for things like email, partials (like headers, footers, menus, etc). Otherwise my views get loosely organized to match the URLs.

Traits are a relatively new feature to PHP. Basically if you have many classes that need the exact same methods inside them to perform a particular task, you could just write them once as a ‘Trait’ and then have each class that needs it use that Trait. For example I have a number of classes which need to perform some string manipulation so the data can be exported in ICS format. So I wrote a Trait that includes the functions, then in any class where I’d like to use those methods I just have the class use the trait.

For your last question, yes, I do… for the most part. So in a task type app I’m working on there are things called Objectives. I’ll have a model in app/Models/Objective.php and a controller in app/Controllers/ObjectivesController.php. Even though I’m separating them by directory, I’m often working with the model and the controller at the same time in my editor so the distinct names helps visualize which tab goes with which class. I keep my models singular, while my controllers use the pluralized name plus Controller.

Hilarious… I’ve been using similar conventions I just came up with on my own…not from reading anything about design patterns, just what made good 'ol sense to me. I always had a class I called “utils” and it sounds a lot like your “traits” example.

I also keep multiple directories for views… I always had “page” and “elem” for partials!

I like your naming conventions… there’s nothing worse than seeing a bunch of files in tabs all with the same name!

So are all your Classes (models, controllers, middleware, etc…) passed to the container?

Just curious what IDE are you using? I’m using coda 2. Not sure if there’s any bells and whistles I can add to it to make slim dev more friendly.

No, not all of my classes are in the container. If they are there or not depends on if they need any sort of configuration settings or other dependencies they have. This is partly because I’m not using Pimple as my container (Slim’s default) and I use PHP-DI instead. Therefore, I can take advantage of Autowiring which uses reflection to find and create the dependencies for some of my classes.

So none of my controllers happen to need an entry in the container, nor do any of my models. Some of my middlewares need an entry because the configuration depends on the environment, etc. If a create() method in my Controller needs the Request object, PHP-DI can figure out where that class is and how to construct it by itself. It is a bit of magic, which some people love and others greatly dislike. But I also think there is a lot of good that comes from learning DI “the hard way” to provide a better architecture and not just cheat with some magic sparkle dust. Part of why I’m using PHP-DI is because the amount of controllers, middleware, etc I was having to define in the container was getting quite long.

I have Coda 2 and use it frequently, but not for most of my PHP projects. I use Coda primarily for projects that are mostly HTML and CSS or when I need to make edits to live sites not under version control or deployment scripts. Lately I’ve been using Atom most of the time. Many people who are doing this much more, and with skills far beyond me swear by PHPStorm.

2 Likes