CodeIgniter Forums
Controller Filters - Input needed - Printable Version

+- CodeIgniter Forums (https://forum.codeigniter.com)
+-- Forum: CodeIgniter 4 (https://forum.codeigniter.com/forum-28.html)
+--- Forum: CodeIgniter 4 Feature Requests (https://forum.codeigniter.com/forum-29.html)
+--- Thread: Controller Filters - Input needed (/thread-65660.html)

Pages: 1 2 3 4


Controller Filters - Input needed - kilishan - 07-07-2016

One feature that I've been thinking about for CI4 is some form of middleware/filter. This has become a popular way of doing things, and can be quite handy.

In some frameworks, like Laravel, the middleware is assigned at the route level, where you say that X middleware runs before or after that route. This works great for frameworks that require all routes to be defined in a routes file.

Other frameworks, like Rails, have controller filters where you can define "filters", or middleware, at the controller level, instead. To me this makes more sense for the CodeIgniter we have, where we do not force people to define every route, and lots of routes are simply determined based on auto-routing to the controller based on the URL.

Both methods have their pros and cons. I had written the Rails way up as a new feature, when albertleao brought up a very good point about the manageability in the long run of this type of a system.

So, I'm looking for input, especially from those of you who have used multiple frameworks in the past. What do you feel are pros and cons to these systems? What other ways have you seen this done?


RE: Controller Filters - Input needed - albertleao - 07-07-2016

Thanks for the thread.

Just to reiterate some of what I said on Git.

Rails filtering system is great in some aspects. It allows you to easily set rules for controllers and you can also tell it to only work on certain methods or ignore other (:only if I remember correctly). The issue with this that my teams always ran into was maintainability as the application grew.  The larger the application became, the more difficult it was to keep track of which routes/controllers/actions had certain filters. Compound that with classes which are extending other classes which have filters and the problem could quickly become a nightmare with cascading rules and so on.

The 'problem' with codeigniter is that the routing system is not required, meaning that a middleware system such as laravels would not work. 

Looking at an application as a whole, it would make more sense for most filters to be placed somewhere before a controller. Not only does this help eliminate any dependency the filter might create on the controller (I think), it allows for the developer to access filters/middleware in a single file elsewhere which in the end will be much easier to maintain. 

If we take the Rails example (http://guides.rubyonrails.org/action_controller_overview.html#filters), they use the `before_action :require_login` as an example. Now lets say we have 50+ controllers that require some form of authentication. That would mean we'd have to make sure that all 50 controllers have that piece of code, which goes against DRY in my opinion. Then lets say that 25 of those controllers need some other form of authentication like an admin role. You could then create another filter, and add them to the admin controllers. Now lets imagine that there are 5 different type of admin roles, and each role might have access to a certain controller, or multiple controllers. And on top of that, let's add that a few of those controllers need to support CORS. All of a sudden you have multiple filters which are being individually set at the controller level, which may or may not specify a certain action.

As you can imagine in the above example, this leads to harder to maintain code.

I would propose a filter/middleware system where you could have a single file to declare your filters and where they would be attached. Whether this happens in the routes.php file or not doesn't matter, but I believe that having that file would be much cleaner and more maintainable than the filter system that Rails currently uses. Is it possible to have CI4 check the routes.php file for middleware/filters even if auto-routing is enabled? Is it possible to have a filters config files?


RE: Controller Filters - Input needed - arma7x - 07-07-2016

Take a look at Sailjs Policies features concept at http://sailsjs.org/documentation/concepts/policies.


RE: Controller Filters - Input needed - albertleao - 07-07-2016

(07-07-2016, 09:14 PM)arma7x Wrote: Take a look at Sailjs Policies features concept at http://sailsjs.org/documentation/concepts/policies.

Doesn't seem too far different than psr-7 middleware featured in some other PHP frameworks.


RE: Controller Filters - Input needed - kilishan - 07-07-2016

(07-07-2016, 09:21 PM)albertleao Wrote:
(07-07-2016, 09:14 PM)arma7x Wrote: Take a look at Sailjs Policies features concept at http://sailsjs.org/documentation/concepts/policies.

Doesn't seem too far different than psr-7 middleware featured in some other PHP frameworks.

It does seem be very similar to what you're asking for.

To play devil's advocate for a moment - is this necessary?

For one thing, it makes things a little more implicit by taking the control and logic away from anything the user sees if they dive in and look at the routes and controller files. Which, in turn, makes it a little more difficult to learn the framework for new users. The thing that would be done in it's place is what we've all done in the past - simply call a check explicitly in our controllers. Now, it's a little more work to manage, true, but diving into the controller we know exactly what's going on.

Is there possibly a solution that keeps it relatively explicit, but still helps solve the problem?


RE: Controller Filters - Input needed - albertleao - 07-07-2016

(07-07-2016, 09:39 PM)kilishan Wrote: It does seem be very similar to what you're asking for.

To play devil's advocate for a moment - is this necessary?

For one thing, it makes things a little more implicit by taking the control and logic away from anything the user sees if they dive in and look at the routes and controller files. Which, in turn, makes it a little more difficult to learn the framework for new users. The thing that would be done in it's place is what we've all done in the past - simply call a check explicitly in our controllers. Now, it's a little more work to manage, true, but diving into the controller we know exactly what's going on.

Is there possibly a solution that keeps it relatively explicit, but still helps solve the problem?

That's the million dollar question Smile. All frameworks nowadays are trying simplify problems like these. It's just going to depend on where the CI council decides to go with it.

Is it necessary? No. In essence, you can just extend your base controller and add all the functionality in there. But again, neither is a validation library, query builder, cache driver as you can code them on your own. At the end of the day, I'm just a developer trying to find the easiest tool to use to get a project going quickly and in a clean and organized manner. The first time I ran into filters in Rails, I thought it was great and it cleaned up my code compared to my very early CI days. Then I saw php middleware and thought it was a massive upgrade in terms of ease of use, readability, and most importantly maintainability.

When building my projects with my devs, one of the criteria I use to decide design philosophies is how much code needs to be written to get it going, and how much code needs to be rewritten to change some functionality. With controller filters, I may have to change 50+ controllers to add or remove a filter, and that's without worrying about :before, :after. With a single filters file, I may have to change 1 line.


RE: Controller Filters - Input needed - albertleao - 07-07-2016

Just for reference for people who may be unfamiliar with Rails or PHP Middleware:

Rails / My guess of what CI4 would look like with Rails like filters


Code:
namespace App\Controllers;

class Companies extends \Codeigniter\Controller {
   protected $before_action = ['auth', 'cors'];

   public function index($company_id) {
     //This method will only execute after running the 'auth' and 'cors'
     //action set in the $before_action variable
   }
}


The filters/middleware I'm talking about has been in various other frameworks and talked about ad nauseum in blogs. Here are some links

What is middleware?
https://philsturgeon.uk/php/2016/05/31/why-care-about-php-middleware/
https://www.sitepoint.com/stackphp-explained/
http://stackphp.com/

Different PHP implementations of filters/middleware:
http://www.slimframework.com/docs/concepts/middleware.html
http://flightphp.com/learn#filtering
https://laravel.com/docs/5.2/middleware


RE: Controller Filters - Input needed - orionstar - 07-07-2016

@kilishan You mentioned that there will be an event system... A default beforeController/afterController events are not enough? The controller and actions name can be passed by arguments. After that the user can do whatever wants with the data.

Besides this, I have my own before-after filter implementation in my company's CMS, MY_Controller:
PHP Code:
    public function _remap($method$params = array())
    {
        
$this->_run_before_filters();

        if (
method_exists($this$method))
        {
            empty(
$params) ? $this->{$method}() : call_user_func_array(array($this$method), $params);
        }
        else
        {
            
show_404();
        }

        
$this->_run_after_filters();
    } 

It's not that pretty, but it's only stayed in because of the legacy apps, now I use events...


RE: Controller Filters - Input needed - kilishan - 07-08-2016

(07-07-2016, 10:55 PM)orionstar Wrote: @kilishan You mentioned that there will be an event system... A default beforeController/afterController events are not enough? The controller and actions name can be passed by arguments. After that the user can do whatever wants with the data.

It's entirely possible, but currently there's no simple way to specify which endpoints the event would affect. It would leave each event doing a bunch of checks, which would get gnarly quick. Additionally, there's no way to stop the controller action from an event, which is a big part of why you'd want to run before filters, anyway. I'm not 100% that returning a redirect from a hook would do anything at this point, without trying it, though I believe it would. So, events come close, but not 100%.


RE: Controller Filters - Input needed - mwhitney - 07-08-2016

I think the whole discussion just comes back to the struggle between two (often opposing) philosophies: discoverability and maintainability.

MVC makes discoverability slightly more difficult, until you learn the conventions of the framework. Some URLs are impossible and it can be difficult to maintain a large site with MVC conventions alone, so you need something beyond naming conventions to deal with routing.

The next step is configurable routing, and to balance maintainability, discoverability, and even ease of coding the solution, you centralize it. The discoverability in this case cuts both ways: it's easy to discover every route in the system, but the user has to be aware that the feature and know where to look.

Events and hooks deal with the same issues.

I could even look at the idea of defining filters within the controller as being similar to using observers in Bonfire models. It's a very powerful feature that I use quite heavily, but the second inheritance comes into play, it's not nearly as discoverable as everyone thinks it is (and it's often not any more discoverable than stuffing it in a config file somewhere if you don't bother to read the docs, anyway).

If the feature and the related configuration file(s) are well-documented, I think that's the best we can do. We can continue doing similar things by leveraging other features, but I think it would be better to give it first-class support and do our best to document it. Then it can be easy to discover every filter/middleware in the request/response chain, and hopefully we can make it easy for users to be aware of the feature and know where to look.