Welcome Guest, Not a member yet? Register   Sign In
porting CI3 base controller to CI4 -- __construct vs initController
#1

I'm brand new to CI4, and trying to port my first CI3 project to it. I have a few base controller classes (AdminController, AJAXController, CLIController) that all extend my base controller, let's call it MY_Controller. MY_Controller defines a __construct function like so:

PHP Code:
class MY_Controller extends CI_Controller
{
    public function 
__construct()
    {
        
parent::__construct();
        
        
// try to make event reporter available immediately with temporarily NULL affected/logged in user id
        // note this refers to $this->db, assuming that has been autoloaded
        
$this->er = new XX_event_reporter($this->dbNULLNULLself::get_ip_address());
        
        
$this->config->load("myconfig");

        
$this->ac = new XX_access_control($this->db);

        
$this->_restore_user_object_from_session();
        
        
        
$logged_in_user_id = ($this->user) ? $this->user->id DB_user::GUEST_USER_ID;
        
// update event reporter with user details
        
$this->er->affected_user_id $logged_in_user_id;
        
$this->er->logged_in_user_id $logged_in_user_id;
        
        if (
ENVIRONMENT == "development") {
            
$this->er->info("Page requested: " $_SERVER["REQUEST_URI"], XX_event_reporter::EVENT_TYPE_UNSPECIFIED);
        }

    } 
// __construct()

// class MY_Controller 

However, I see that the CI4 BaseController class has an initController function and doesn't define any __construct function. I have questions:

1) Why does CI not define a __construct function instead? It seems unorthodox to define some other function.
2) If I'm porting MY_Controller to CI4, should I try and move all the contents of my __construct function instead to an initController function?
3) Should I keep the MY_Controller class or should I just merge all of its contents into CI4's BaseController class? Performance considerations suggest that combining the two would be more efficient, but I'm concerned about possible side effects or undesirable behavior and would like to adhere to best practices.
4) Does anyone have any other recommendations or advice for porting controllers?
Reply
#2

(12-08-2020, 10:27 AM)sneakyimp Wrote: 1) Why does CI not define a __construct function instead? It seems unorthodox to define some other function.
2) If I'm porting MY_Controller to CI4, should I try and move all the contents of my __construct function instead to an initController function?
3) Should I keep the MY_Controller class or should I just merge all of its contents into CI4's BaseController class? Performance considerations suggest that combining the two would be more efficient, but I'm concerned about possible side effects or undesirable behavior and would like to adhere to best practices.
4) Does anyone have any other recommendations or advice for porting controllers?

I don't claim to have all the answers, but:

1) __construct is a PHP magic function at creation time. Calling a function after you know it is created makes things easier I guess.
2) If you come from CI3 and do stuff in __construct to check authentication etc you should look into filters instead.
3) In my projects I use BaseController for *very* basic stuff and then extend that to AdminController and UserController. Performance-wise I haven't seen any side-effects running on PHP 7.3.
4) IMHO my Controllers have been fatter in CI4 than in CI3, but that's mainly due to the stricter Model class. In CI3 I put much logic into the model because that knew about the data. Now I have created multiple Services instead.
Reply
#3

Thanks for your response!
(12-08-2020, 01:11 PM)tgix Wrote: 1) __construct is a PHP magic function at creation time. Calling a function after you know it is created makes things easier I guess.
You really think so? You have to create the new object and then call init function (two lines of code) whereas the __construct function just lets you supply the parameters to the constructor. The only reason to have a separate init function is if you need to perform some operation that cannot be accomplished in a constructor. I wonder if perhaps the internal CI framework might be performing some other setup operations on the controller which must precede this init function (e.g., injecting application state, etc.).

(12-08-2020, 01:11 PM)tgix Wrote: 2) If you come from CI3 and do stuff in __construct to check authentication etc you should look into filters instead.
In my case the point of the authentication-related operations in the constructor was to establish whether a user was logged in or not before performing any other operations. Could you be more specific about how filters relate to authentication?

(12-08-2020, 01:11 PM)tgix Wrote: 3) In my projects I use BaseController for *very* basic stuff and then extend that to AdminController and UserController. Performance-wise I haven't seen any side-effects running on PHP 7.3.
My user controller and admin controller have a lot of functions in common (in particular the checking whether there's an authenticated user, establishing basic data for views, functionality to easily obtain an emailer object). I admit that my performance considerations are based on some advice I heard long ago that having too deep an inheritance hierarchy in PHP can cause performance worries. I can't even find the article now.

(12-08-2020, 01:11 PM)tgix Wrote: 4) IMHO my Controllers have been fatter in CI4 than in CI3, but that's mainly due to the stricter Model class. In CI3 I put much logic into the model because that knew about the data. Now I have created multiple Services instead.
I do not follow what you've said here. Could you elaborate?
Reply
#4

To check if a user is logged in or not you need to use Controller Filters.
What did you Try? What did you Get? What did you Expect?

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

(12-09-2020, 02:29 PM)sneakyimp Wrote: You really think so? You have to create the new object and then call init function (two lines of code) whereas the __construct function just lets you supply the parameters to the constructor.
My javascript framework is doing exactly this - create the object and then allows me to hook into the states in the object's life-cycle (initialize, viewmodel ready, destroy etc). For the framework I would expect this to be better. CI4 is stricter with namespaces and loading as well.

(12-09-2020, 02:29 PM)sneakyimp Wrote: Could you be more specific about how filters relate to authentication?
Filters themselves don't do authentication. Relating to my answer to the question above - filters allows you to participate in the life-cycle of the request. Filters allow you to intercept the request before it gets to your controller and run your code (check for authentication perhaps) before running your controller code. You can also create an after filter to modify the response before being sent to the client. https://codeigniter4.github.io/userguide...lters.html

(12-09-2020, 02:29 PM)sneakyimp Wrote: My user controller and admin controller have a lot of functions in common ...
CI4 forces you to use a cleaner object oriented programming style. Going from CI3 to CI4 (and from PHP 5.4 to 7.x) I learned a lot of the OO programming features of PHP. I use traits extensively for common functions and they are a good way to help you program DRY (https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) I also use CI4's Services and found them convenient to connect Filters and Controllers, especially for authentication.
Performance-wise I have stopped worrying about all this "extra", but YMMV.
My production environment is based on 7.3 with opcache running in Docker containers in AWS. The performance issues I currently see are all database related and not from PHP as far as I can tell.

(12-09-2020, 02:29 PM)sneakyimp Wrote: I do not follow what you've said here. Could you elaborate?

CI4's models are more like the Models I have been using before - they strictly model the data and with the introduction of Entities the data is more consistent and accessible than in CI3. In CI3 I used Models to store code that was not acting on the data itself, but was connected to the data the model represented.
What I meant with "fatter controllers" - I have been taught the mantra of "Skinny Controller, Fat Model" for my MVC-development and I like that. Execution starts in the Controller and a skinny (less code) Controller allows you to quickly understand what is going on. The Controller then calls logically named functions in the Fat Model to do the stuff.
In CI4 my Controllers are fatter and actually do some database calls etc. To fight this I am using Services and traits.
Example from CI3: I have a User model that not only acts on the User's data but also listed user classes and other non-data related but user-related stuff. With CI4 I created a module DBHelpers with static functions called from the Controller.

I have been working a couple of months on a REST-ful AJAX back-end in CI4 and I have a dream to compile this into a tutorial. Hopefully there will be time soon.
Reply
#6

(This post was last modified: 12-10-2020, 02:00 PM by sneakyimp.)

(12-09-2020, 10:54 PM)tgix Wrote: Filters allow you to intercept the request before it gets to your controller and run your code (check for authentication perhaps) before running your controller code. You can also create an after filter to modify the response before being sent to the client. https://codeigniter4.github.io/userguide...lters.html
Thank you for the clarification. I've read that page on Filters and am not sure I'm convinced that using filters for authentication is all that great. Granted, I haven't fully absorbed the concept of Filters, but it seems like poor organization to set up something as fundamental as authentication in a configuration file named Config/Filters.php which, in turn refers to authentication logic in Filters/Auth.php (or whatever). I haven't had the full awakening yet about the utility and usage patterns of filters, and I can see that there seems to be some economy/expressiveness/routing in the compact arrays of the Config/Filters class, but one of the great advantages of codeigniter IMHO is how it very transparently organizes code such that you can easily look at a url for a website and quickly locate the corresponding controller. I also like when you can open a controller, you can look at its parent class (e.g., BaseController or UserController or AdminController)  and, by hitting the quick key in your IDE, you can quickly locate the logic that controller uses for authentication and quickly get an idea of what authentication is happening or what other logic is at play. I'm a huge fan of Javadoc comments and tend to dislike the use of scalar string keys and clever-but-opaque code that can skin a cat five different ways but which often yields cryptic errors whose source is hard to locate. That coding style is a lot more like Laravel, which I find somewhat impenetrable and pedantic. I like frameworks where it's easy to find the responsible PHP file handling a particular response and where the code clearly indicates what is happening and where the comments effectively use javadoc and type hinting so you get all the nice autocomplete features and documentation. Where you can just hit F3 and it'll take you right to the code so you can read it.

I realize I may be missing some amazing elegant concept, and would be happy to hear any counterpoint to these observations.

(12-09-2020, 10:54 PM)tgix Wrote: CI4 forces you to use a cleaner object oriented programming style. Going from CI3 to CI4 (and from PHP 5.4 to 7.x) I learned a lot of the OO programming features of PHP. I use traits extensively for common functions and they are a good way to help you program DRY (https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) I also use CI4's Services and found them convenient to connect Filters and Controllers, especially for authentication.
I'm curious to see how CI4's OOP is "cleaner." So far, I see a lot of cumbersome namespacing and a lot of ways it breaks your CI3 project.

(12-09-2020, 10:54 PM)tgix Wrote: In CI4 my Controllers are fatter and actually do some database calls etc. To fight this I am using Services and traits.
I really appreciate your explaining this, but it'll take me some time to absorb. In my CI3 project, I didn't hesitate to make model classes that combine the atomic, db-based model classes using Aggregation/Composition/Association. I basically had two types of model classes:
1) auto-generated DB_x classes, one for every db table, which are basic-but-extensible interfaces to my database. I wrote a script to read my db and automatically create one class for every table.
2) ad-hoc model classes as needed to combine the db classes into my business logic.

(12-09-2020, 10:54 PM)tgix Wrote: I have been working a couple of months on a REST-ful AJAX back-end in CI4 and I have a dream to compile this into a tutorial. Hopefully there will be time soon.
Do it! The world can always use more discourse and illustrations of coding techniques.

(12-09-2020, 09:49 PM)InsiteFX Wrote: To check if a user is logged in or not you need to use Controller Filters.
I disagree that you "need" to use Controller Filters. This is an option, but can be accomplished other ways. If you mean to suggest it is the most effective way, I'd be happy to hear why you think so. I'm not sure that I agree it's a best practice.
Reply
#7

(This post was last modified: 12-11-2020, 03:00 AM by tgix. Edit Reason: Updated github link )

Thank you for an interesting and delightful discussion!

(12-10-2020, 01:58 PM)sneakyimp Wrote: Thank you for the clarification. I've read that page on Filters and am not sure I'm convinced that using filters for authentication is all that great.

(12-10-2020, 01:58 PM)sneakyimp Wrote: I'm curious to see how CI4's OOP is "cleaner." So far, I see a lot of cumbersome namespacing and a lot of ways it breaks your CI3 project.

While enjoying my morning coffee I put together a more detailed description of my CI4 back-end and how I have implemented the filters. I am not a blog-artist so it's here in github - https://github.com/tangix/ci4-zen-zone/t...ng-filters

Maybe a start as a blogger, who knows?

I have been living in PHP 5.x for too long and since CI3 didn't "force" me to use OOP I didn't. I have come to appreciate traits, interfaces and namespacing as it makes the code cleaner and more robust. For my development i use PHPStorm and find many of the code-navigation and -completion functions very helpful.

(12-10-2020, 01:58 PM)sneakyimp Wrote: I really appreciate your explaining this, but it'll take me some time to absorb. In my CI3 project, I didn't hesitate to make model classes that combine the atomic, db-based model classes using Aggregation/Composition/Association. I basically had two types of model classes:
1) auto-generated DB_x classes, one for every db table, which are basic-but-extensible interfaces to my database. I wrote a script to read my db and automatically create one class for every table.
2) ad-hoc model classes as needed to combine the db classes into my business logic.
In my project described above I am using Entities extensively to transform data between front- and back-end. Many of the UI elements for example use JSON arrays for multi-pickers. I have created by own BaseEntity handling casts between arrays (from the front-end) and comma separated strings (stored in the database). The map directly

(12-10-2020, 01:58 PM)sneakyimp Wrote: Do it! The world can always use more discourse and illustrations of coding techniques.
First step taken - https://github.com/tangix/ci4-zen-zone
Reply




Theme © iAndrew 2016 - Forum software by © MyBB