Header/footer conflicting with JSON

I implemented header / footer html as mentioned here: Header/footer in php-view with Slim v3

Now when I make an ajax call to get JSON the header / footer html are being included!

return $response->withJson($out);

What’s is the best way to handle this with SLIM 3.* ?

Similar to the other thread about Middleware, you could use the same approach. You could create a Middleware that sets the header and footer, then apply that Middleware to routes that need it, and don’t apply it to any routes that don’t need it.

Thanks for the suggestion.
I’ve been working on the middeware parts and am having some trouble for the sake of simplicity I’m keeping most things in my controller like this:

//load application settings
require 'config.php';

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
use Slim\Views\PhpRenderer;

$config['displayErrorDetails'] = true;
$config['addContentLengthHeader'] = false;

$config['db']['host']   = "127.0.0.1";
$config['db']['user']   = "root";
$config['db']['pass']   = "";
$config['db']['dbname'] = "my_db";

// Create the Slim application using our container.
$app = new \Slim\App(["settings" => $config]);

$container = $app->getContainer();

//database
$container['db'] = function($c){ 
    //setup NOTORM
    $db = $c['settings']['db'];
    $pdo = new PDO("mysql:host=" . $db['host'] . ";dbname=" . $db['dbname'], $db['user'], $db['pass']);
    $db = new NotORM($pdo);
    $db->debug = true;
    return $db;
};


require 'models/Header_footer.php';

$container['view'] = new PhpRenderer(BASE_PATH . "/views/page/");
$container['elem'] = new PhpRenderer(BASE_PATH . "/views/elem/");


$app->get('/dashboard', function ($request,$response,$args) {
    return $this->view->render($response, "dashboard.html", []);
})->add('App\Middleware\Header_footer');

$app->run();
exit;

and my Header_footer class looks like this:

namespace App\Middleware;

class Header_footer{
    public function __invoke(Request $request, Response $response, $next){
        $renderer = $this->get('elem'); 
        $renderer->render($response, 'header.php');
        $next($request, $response);
        $renderer->render($response, 'footer.php');
        return $response;
    }
}

I’m getting this error:

Catchable fatal error: Argument 1 passed to App\Middleware\Header_footer::__invoke() must be an instance of App\Middleware\Request, instance of Slim\Http\Request given in C:\xampp\htdocs\admin\models\Header_footer.php

When I remove “Request and Response” (typecasting?) I then get this error:

Fatal error: Call to undefined method App\Middleware\Header_footer::get() in C:\xampp\htdocs\admin\models\Header_footer.php

I’ll be quite honest I had gotten started with SLIM 2 (there were tons of examples on the web) and everything was pretty much straight forward. SLIM 3 has so many layers of abstraction it’s as if it’s not even PHP (at least the easily readable straight forward language most of us know). I’m probably just behind in all the hip new PHP conventions but I feel like a newb although I’ve been developing in PHP for the past 10 years.

As to the last part, I was in the same situation as you about a year ago. I had been doing procedural PHP scripts for about ten years and didn’t understand much at all about OOP, namespaces, let alone things like type hinting, PSR-#, DICs, etc. I’ve learned quite a bit over the past year or so. Looking back on code I wrote just 18 months ago looks like a tangled spaghetti mess that I hope nobody else ever sees.

In your Header_footer middleware class, you can only type hint an object (Request $request) if PHP knows what Request is. That is where the use statements come in. Think of them similar to PHP’s require or import functions, but more like how an alias works on Macs or a shortcut works on Windows. Basically you need to tell PHP what object (class) Request is.

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;

So we’re telling PHP that anytime we typehint Request we really mean that the $request is an instance of Psr\Http\Message\ServerRequestInterface.

The error you see is because you didn’t explicitly tell the invoke method what Request was. Since you didn’t tell it, it assumes that it is a class within the same namepace, in this case App\Middleware, so it assuming Request can be found in App\Middlware\Request.

Some resources if you would like to get up to speed on newer PHP conventions…

Excellent explanation tflight.

I added:

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;

to my Header_footer.php class and I’m one step closer

I had already added those two lines to the top of my controller and thought that was enough but, silly me - my mistake of course.

Now I’m having trouble with $this->get() not being found inside the class. Curiously this same code worked fine when I was adding it outside of a class as a regular function.

> Fatal error: Call to undefined method App\Middleware\Header_footer::get()

Have any ideas what’s wrong now?

thanks!

In your Header_footer class, you have:

 $renderer = $this->get('elem'); 

$this refers to the Header_footer class, which doesn’t have a get() method. If elem is something from your container, you will need to pass it to the Middeware and accept it in the Middlware’s constructor.

class Header_footer{
    protected $elem;

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

    public function __invoke(Request $request, Response $response, $next){
        $renderer = $this->elem;
        $renderer->render($response, 'header.php');
        $next($request, $response);
        $renderer->render($response, 'footer.php');
        return $response;
    }
}

(And you will likely need to modify your container to give whatever $elem is to App\Middleware\Header_footer.)

That makes total sense but I’m not instantiating the Header_footer class like a good 'ol:

$hf = new Header_footer($containter['elem']);

So I’m not sure how to pass in the container element?

Slim seems to instantiate the class via:

->add('App\Middleware\Header_footer')

yep, confused again. It’s amazing how little there is out there about SLIM v3.* everything I find is on V2 (I’m not being lazy)

I can’t even find the docs. The home page has some examples but they’re really not docs in the traditional sense of “this is function xyz: and here’s some examples”

Again I appretiate all your help :slight_smile:

Is elm in your container? If not… you will want to put an entry for it in there. Then, once that is done, you can define your Middleware in the container, perhaps like this.

$container['Header_footer'] = function ($c) {
    $header_footer = new \App\Middleware\Header_footer($c['elm']);
    return $header_footer;
};

Then in the routes where you want the middleware applied…

->add('Header_footer')

This depends on 'elem' being defined in your container.

1 Like

You solved my problem my friend! And I will not even pretend to understand all of these abstract layers but it seems to me I’ll never be calling a class like:

$mcc = new my_cool_class();

At least not on it’s own out in the open… a class (especially one that needs $app->db) will always be stored inside the container so that the db and any other objects such as the $app itself are available to the class.

I have no idea how PHP handles memory but it seems like I’ll be loading a ton of stuff into that container and not necessarily using it.

Ahhh… but that is the very point of the container. The container is where you tell PHP “this is how you construct this object, when I ask for it”. The container is kind of like a box of recipes, with each definition in the container an individual recipe, made up of ingredients (objects, config settings, or other container entries.) Having a recipe for a cake isn’t the same thing as having baked a cake. :slight_smile:

PHP doesn’t load all of those things into memory. They only get loaded when another part of your app asks for one. One class says to PHP “hey, I need one of these objects” and PHP looks in the container, figures out how to build it, and passes it to your class all ready to go.

So you can load as much as you want in the container and the beautiful thing is that it will not be loaded into memory until you ask for an object from it.

1 Like

You’re a good teacher @tflight , thanks a bunch Tim. :wink:

1 Like

Happy to help however I can!