Welcome Guest, Not a member yet? Register   Sign In
IDEA: Controller Extended by Default
#1

(This post was last modified: 03-18-2019, 07:34 AM by MGatner. Edit Reason: location comment )

I've been reading on the forums a lot, and one thing I keep seeing: there are a lot of requests solved by extending the controller and then using a new base controller to recreate some of the CI3-style "autoload". From a development standpoint, it makes sense to extend the controller initially even if you don't have any modifications to the base yet since making the change down the road involves going back and editing every controller.

So, would it make sense to ship with a basic extended Controller (e.g. app/BaseController.php) that was used by app/Controllers/Home.php and recommended by the User Guide? Doesn't seem that it would cost anything and would set many people up for a best practice.

(reference: https://codeigniter4.github.io/CodeIgnit...re-classes)

EDIT: I suppose a better location would be something like app/Libraries/BaseController.php, or in a new folder like "Core" or "Extensions" in app/.
Reply
#2

It's a controller and should go into the app/controllers folder.

./app
/controllers
-- Base.php
-- Backend.php
-- Frontend.php
-- Home.php

That's how I have mine setup.
What did you Try? What did you Get? What did you Expect?

Joined CodeIgniter Community 2009.  ( Skype: insitfx )
Reply
#3

That's not a bad idea. And, while I would structure my controllers pretty close to how InsiteFX showed, I also would turn off auto-routing. With AutoRouting enabled they could theoretically access methods in the Base controllers if they weren't careful. Putting it in a different location would make sense for that reason, I think. But i'm not sure off the top of my head what an ideal location is for that. I would definitely be interested in what everyone else thinks about this.
Reply
#4

None of the locations seem ideal. I didn't want to put it in app/Controllers/ because of the routing issue @kilishan mentioned. I had it in app/Core/ but then I disliked running a whole new namespace that would (probably?) only ever have one class. For now I have it at app/BaseController.php, with this corresponding "skeleton":

PHP Code:
<?php namespace App;

use 
CodeIgniter\Controller;

class 
BaseController extends Controller
{
    protected 
$helpers = [ ];
    
    public function 
__construct()
    {
    
    }

Reply
#5

(03-18-2019, 08:18 AM)InsiteFX Wrote: It's a controller and should go into the app/controllers folder.

./app
   /controllers
   -- Base.php
   -- Backend.php
   -- Frontend.php
   -- Home.php
I agree with this method. to protect from direct access we can define methods as private methods.
Reply
#6

I do like that this puts it in the same directory and namespace as the controllers that will extend it. I think it will still need a route though, because otherwise visiting example.com/base will give cause "Controller method is not found: index" since the controller itself is valid.
If everyone is feeling good about this I will open a pull request as the next part of the conversation. Here is my updated skeleton proposal given this new location:

PHP Code:
<?php namespace App\Controllers;

use 
CodeIgniter\Controller;

class 
Base extends Controller
{
    protected 
$helpers = [ ];
    
    public function 
__construct()
    {

    }


And I'd recommend adding this to system/Config/Routes.php:
PHP Code:
// Prevent access to Base controller
$routes->add('base/(:any)', function()
{
    throw \
CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}); 


*Note: forPageNotFound() currently incorrectly requires a parameter. I have a pull request to fix this: https://github.com/codeigniter4/CodeIgniter4/pull/1842
Reply
#7

Let's call it BaseController. That way for people auto-routing it shouldn't conflict with anything they want to build. Don't add the constructor, though. That's for each controller to use on their own. Instead, override the initController method:

Code:
class Base extends Controller
{
   protected $helpers = [];

   public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
   {
        parent::initController();

       // Autoload any models, libraries, etc, here.
   }
}

Adding that to the system routes is a good idea, also.

Finally - please add a comment in the class docblock what it's intended usage is, a small example maybe, and a warning that all methods should be protected or private for security reasons.

And updated docs, of course.

Then I'm ok with that being added.
Reply
#8

@kilishan I had to fully-namespace the type hints and pass the parameters to the parent class:

PHP Code:
    public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
    {
        
parent::initController($request$response$logger); 

I'm curious about your comment "Autoload any models, libraries, etc, here." - since there's no super-object anymore, what does loading anything in the base do, as it won't be accessible to the children controllers? Or is the thought to create protected variables to receive the objects? E.g.:

protected $userModel;

In initController:
$this->userModel = new UserModel();

Then in any Controller that extends BaseController:

$user = $this->userModel->find($userid);

... or am I missing something?
Reply
#9

The example you show is what I meant. That was a big part of why you brought this up, right? To provide some place to autoload models, libraries etc for that controller?
Reply
#10

That's correct! I was just clarifying, this is relatively new to me. Smile
Pull request sent: https://github.com/codeigniter4/CodeIgniter4/pull/1847
Feel free to edit anything or request edits from me.
Reply




Theme © iAndrew 2016 - Forum software by © MyBB