slimphp / Slim

Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs.
http://slimframework.com
MIT License
11.98k stars 1.95k forks source link

What happened to Hooks and SetData? #1583

Closed cleartext closed 9 years ago

cleartext commented 9 years ago

Hi All,

Loved SLIM 2, so easy to get started for an occasional developer like me but v3 is proving a bit harder to get to grips with.

In v2 I had a hook to pull page meta data from a table, based on the URL request, which was then dropped in via a Twig page header template, like this;

$app->hook('slim.before.router', function () use ($app) { $req = $app->request; $path_is = $req->getPath(); $metadata = ORM::for_table('metadata') ->select('*') ->where('url', $path_is) ->find_one(); $app->view->setData( array( 'metadata' => $metadata )); });

Now that Hooks have gone I've spent some time on middleware, and seeing that setData is going to be dropped I've been looking at Set on the View class. However so far I've not been able to reproduce the Hook using middleware.

Any help would be appreciated, even if it's 'read this' and a URL :)

thanks,

David. PS - It's great to see open source projects that are simple and easy to get into, they are essential for those of us who aren't full time developers but like to keep our hand in.

akrabat commented 9 years ago

Here's some middleware that assumes that you're using Twig-View and the you have registered the Twig-View object in the Container as 'view':

function ($request, $response, $next){
    $path = $request->getUri()->getPath();
    $metaData = Orm::for_table(/*..*/);

    $this->get('view')->getEnvironment()->addGlobal('metadata', $metadata);

    return $next($request, $response);
}

(Buyer beware: code written off the top of my head...)

silentworks commented 9 years ago

Hi @cleartext the code above should work. Also thanks for the feedback, I think this is something we will need to get sorted in the documentation, because as you say it feels somewhat harder to get into Slim 3 than it was with Slim 2, I would like to get some more of your feedback on anything else you are finding issues with so we can write some Cookbook style documentation around those areas.

cleartext commented 9 years ago

Thanks guys, that doesn't work as is. I don't see an addGlobal in Twig-View at all, although the pointer to $this->get('view') helps a lot in that at least now I have a valid View object. Will keep trying.

Will also feedback any other issues I have, with my amateur hat on :)

akrabat commented 9 years ago

The addGlobal() is in Twig_Environment which is why we getEnvironment() from the Twig-View

silentworks commented 9 years ago

Or you should be able to do

$this->get('view')['metadata'] = $metadata;
cleartext commented 9 years ago

There's something very odd happening here, I have;

// Create container $container = new \Slim\Container();

// Register component on container $container['view'] = function ($c) { $view = new \Slim\Views\Twig('views/'); $view->addExtension(new \Slim\Views\TwigExtension( $c['router'], $c['request']->getUri() )); return $view; };

// Create app $app = new \Slim\App($container);

$app->add(function ($request, $response, $next) { $response = $next($request, $response); $host = $request->getUri()->getHost(); $this->get('view')->getEnvironment()->addGlobal( 'host', $host ); return $response; });

Then in my template;

Experimental Project 55 {{ host }}

Just to test for now... still no joy. All runs fine, but 'host' appears to be empty.

Just ran a composer update to be sure I have the latest, I did get a small twig update, but no change. I think I need to leave this for a day and come back to it, been spending too long thinking too hard I think!

akrabat commented 9 years ago

You are calling $next too early in your middleware - i.e. before you set the global.

Change to:

$app->add(function ($request, $response, $next) {
    $host = $request->getUri()->getHost();
    $this->get('view')->getEnvironment()->addGlobal( 'host', $host );

    $response = $next($request, $response);
    return $response;
});

This way the 'host' value is added to Twig's global registry before you render it within your .twig file.

cleartext commented 9 years ago

Ah, that fixed it. I knew I'd been looking at this far too long.

I should probably get into the habit of returning $next($request, $response) maybe?

A disadvantage of working from home, no-one close by to turn around to and ask for a quick look, thanks for spending the time on this for me Rob and Andrew.

akrabat commented 9 years ago

You are very welcome :)

Have a look at https://speakerdeck.com/akrabat/building-an-api-with-slim-3?slide=30 for the general skeleton of a piece of middleware.

Personally I like to use return $next($request, $response); when the middleware does something before dispatch.

cleartext commented 9 years ago

That helps, penny drops on 'middleware' now, that's the first time I've seen it explicitly said that 'is code that sits between request and response'. v3 Docs could explain this a bit better, I will re-read v2 docs as well. rgrds, David.

geggleto commented 9 years ago

I know im a bit late to the parttt-ehhhh, but I just wanted to share some config with y'all.

in the init code for Twig-View...

        foreach ($c['twigConfig'] as $name => $value) {
            $view->getEnvironment()->addGlobal($name, $value);
        }

And in the container...

    'twigConfig' => [
        'title' => 'My Site Title',
        'description' => '',
        'author' => '',
        //...
    ],
alexweissman commented 8 years ago

This is actually one of the main things holding me back from upgrading to Slim 3 (everything else looks really great!)

If there is some equivalency between hooks and middleware, would it be possible to implement generic Slim2-style hooking functionality as a piece of Slim3 middleware?

geggleto commented 8 years ago

@alexweissman sadly no. Middleware are a type of hook though, the only thing you get in slim 3 is BEFORE or AFTER the route is executed.

Keep in mind that middleware is executed as a stack, so you can still setup which middleware gets called in what order.

alexweissman commented 8 years ago

Hmm, so, for those of us building applications that require a means of hooking-in functionality (to support plugins, themes, etc), should we implement our own hooks then? Is there a good third-party package for this?

geggleto commented 8 years ago

I have no idea why you need hooks to be honest.

Can you provide an example of why you can't use a middleware and require a hook?

alexweissman commented 8 years ago

Well, you said so yourself - middleware ⊂ hooks, but not the other way around. Lets take my framework UserFrosting, for example, which is built on Slim. I have a "register account" route, which might have the following pseudocode:

$app->post('/account/register/?', function () use ($app) {    
    // Validate user data
    ...
    // Hash new user password
    ...
    // Create new User model
    ...
    // Store new user to database
    ...
    // Send confirmation email to new user
    ...
});

Suppose I want to allow third-party developers to easily hook into register, and modify some aspect of the registration process before sending the email to the new user (let's take, as an example, setting up a gravatar icon or something). In Slim 2, this was easily done via:

    // Store new user to database
    ...
    // Send confirmation email to new user
    ...

    $app->applyHook('registration.send_confirmation_email.before');

    // Send confirmation email to new user
    ...
OptimusCrime commented 8 years ago

You can pretty easily implement this by using https://packagist.org/packages/symfony/event-dispatcher .

alexweissman commented 8 years ago

Thanks! That looks like a good alternative. Maybe this package could be an official recommendation in the Slim3 docs, as a replacement for those who truly need hooks?

akrabat commented 8 years ago

Maybe this package could be an official recommendation in the Slim3 docs, as a replacement for those who truly need hooks?

We'd love a PR to Slim-Website! At a guess, this would be good to note in the /docs/start/upgrade.md file.

geggleto commented 8 years ago

I'm glad you got this resolved. I hadn't realized people were using Hooks as an event dispatcher. Very interesting!

alexweissman commented 8 years ago

Ahhh, I see! Like many things, this is an issue of vocabulary ;-) I'm not sure if the use of the word "hook" in this way was invented by Wordpress, but it certainly was popularized by it:

Many WordPress Plugins accomplish their goals by connecting to one or more WordPress Plugin "hooks". The way Plugin hooks work is that at various times while WordPress is running, WordPress checks to see if any Plugins have registered functions to run at that time, and if so, the functions are run. These functions modify the default behavior of WordPress.

Not that WP is a paragon of good programming and software design practices (it's definitely not), but WP refugees are everywhere, and would likely understand "hook" to mean "event dispatcher".