Welcome Guest, Not a member yet? Register   Sign In
CI 4.4.0 modules shared services not working
#1

Hi guys,
Long story short:
I have multiple modules. Base module has Alert class which collects all system messages while loading through Controller, Models, logic etc.
That is why i need shared instance functionality and Services was perfect for that.
At the controller end, when layout view loads, i want to print all collected messages on a screen.

This approach did work in CodeIgniter 4.3.7 version. Just did upgrade to 4.4.0 and this does not work anymore.

Below is simplified code to catch the idea

App\Config\Modules.php
PHP Code:
class Modules extends BaseModules
{
    public $enabled true;

    public $aliases = [
        'events',
        'filters',
        'registrars',
        'routes',
        'services',
    ];


App\Config\Autoload.php
PHP Code:
class Autoload extends AutoloadConfig
{
    public $psr4 = [
        APP_NAMESPACE => APPPATH// For custom app namespace
        'Config'      => APPPATH 'Config',
        'Modules'    => ROOTPATH 'Modules'// my custom modules
    ];


Modules\Base\Config\Services.php
PHP Code:
use Modules\Base\Libraries\Alert;

class 
Services extends BaseService
{
    public static function Alert(bool $getShared true): Alert
    
{
        if ($getShared) {
            return static::getSharedInstance('Alert') ?? new Alert();
        }

        return new Alert();
    }


Modules\Base\Libraries\Alert.php
PHP Code:
namespace Modules\Base\Libraries;

class 
Alert
{
    public array $messages = [];

    public function set(string $typestring $message): void
    
{
        $this->messages[] = ['type' => $type'message' => $message];
    }

    public function getMessages(): array
    {
        return $this->messages;
    }


Modules\Base\Contollers\BaseController.php
PHP Code:
class BaseController extends Controller
{
    public Alert $alert;

    public function initController(RequestInterface $requestResponseInterface $responseLoggerInterface $logger)
    {
        $this->alert = \Modules\Base\Config\Services::Alert(); // load shared isntance. In real life this is the 1st isntance

        $this->alert->set('danger''This message will show in red!');


        dd(service('alert')); 
    }


response is empty "service(...) null"


Anywhere in frontend layout views i want to display potential system messages and shared instance is null

PHP Code:
dd(service('Alert')->getMessages());
ErrorCall to a member function getMessages() on null 


As far as i understand, Services can load class outside of App\Config\Services scope but can't keep it or recognize as shared instance for multiple use down the code

Am i missing anything? Any ideas? Suggestions?
Reply
#2

(This post was last modified: 08-30-2023, 04:30 AM by ozornick.)

In Services:
...
return static::getSharedInstance('Alert');
...
Remove ?? new Alert()

service('Alert') to lowercase service('alert')
Simple CI 4 project for beginners codeigniter-expenses
Reply
#3

Did you really work with v4.3.x?
App\Config\Autoload.php should be:

PHP Code:
    public $psr4 = [
        APP_NAMESPACE => APPPATH// For custom app namespace
        'Config'      => APPPATH 'Config',
        'Modules\Base'    => ROOTPATH 'Modules/Base'// my custom modules
    ]; 

See https://codeigniter4.github.io/CodeIgnit...namespaces
Reply
#4

(This post was last modified: 08-30-2023, 05:30 AM by davis.lasis.)

(08-30-2023, 04:27 AM)ozornick Wrote: In Services:
...
return static::getSharedInstance('Alert');
...
Remove ?? new Alert()

service('Alert') to lowercase service('alert')

Did you replicate this? 
I tried your options - no luck, it does not work.


I changed Autoload

PHP Code:
'Modules'    => ROOTPATH 'Modules'

to
PHP Code:
'Modules\Base'    => ROOTPATH 'modules\Base'



and now it throws infinite loop error

PHP Code:
Fatal errorAllowed memory size of 536870912 bytes exhausted (tried to allocate 262144 bytesin ..\system\Config\BaseService.php on line 267 



PHP Code:
public static function serviceExists(string $name): ?string
    
{
        static::buildServicesCache(); // this is line 267
        $services array_merge(self::$serviceNames, [Services::class]);
        $name    strtolower($name);

        foreach ($services as $service) {
            if (method_exists($service$name)) {
                return $service;
            }
        }

        return null;
    

Old version (4.3.7) serviceExists() and buildServicesCache() code are the same. So it should be something related to Module autoloading... Or maybe caching is changed, cause i never use it

(08-30-2023, 04:34 AM)kenjis Wrote: Did you really work with v4.3.x?
App\Config\Autoload.php should be:

PHP Code:
    public $psr4 = [
        APP_NAMESPACE => APPPATH// For custom app namespace
        'Config'      => APPPATH 'Config',
        'Modules\Base'    => ROOTPATH 'Modules/Base'// my custom modules
    ]; 

See https://codeigniter4.github.io/CodeIgnit...namespaces

Weird question. Yes i do work with CI since v2 till now, always updating main core with newest version.

I had magic legacy method in Autoload which loaded all modules and Services worked fine in each module

PHP Code:
$modules array_filter(glob('../Modules/*'), 'is_dir');

        if (!empty($modules)) {
            foreach ($modules as $key => $module_path) {
                if (str_contains($module_path'/')) {
                    $split_path explode('/'$module_path);
                    if (array_key_exists(2$split_path)) {
                        $this->psr4['Modules\\'.$split_path[2]] = ROOTPATH.'modules/'.$split_path[2];
                    }
                }
            }
        
Reply
#5

You customized the autoloading, so it seems there is something wrong.
and the line "'Modules'    => ROOTPATH . 'Modules', // my custom modules" in $psr4 should be removed.

I think there is no change in the framework code for Module service discovery in v4.4.0.
So I don't know why your app stop working.

You can check the diffs: https://github.com/codeigniter4/framewor...b10da1c710
Reply
#6

(08-30-2023, 02:26 PM)kenjis Wrote: You customized the autoloading, so it seems there is something wrong.
and the line "'Modules'    => ROOTPATH . 'Modules', // my custom modules" in $psr4 should be removed.

I think there is no change in the framework code for Module service discovery in v4.4.0.
So I don't know why your app stop working.

You can check the diffs: https://github.com/codeigniter4/framewor...b10da1c710

Hi, Kenjis
Not so sure if main Modules path can be removed. I will make a lot of testing and debuging.
I will update on monday / tuesday.

Yesterday i think i figured out that my custom registrar tries to connect to DB and load model from module which is not loaded yet. And i think that makes infinite loop on Services.
Misleading, but i will make a bunch of tests and update here.

Thanks
Reply
#7

I assumed your modules are like these:

Code:
Modules/Foo/ ... Module Foo
Modules/Bar/ ... Module Bar


But if you also use `Modules/` as one module:
Code:
Modules/ ... Main Module
You cannot remove the Module path from $psr4.

But it is not recommended. Because it is confusing.

Code:
Modules/Bar/         ... Module Bar
Modules/Config/      ... Config folder in the main Module
Modules/Controllers/ ... Controllers folder in the main Module
Modules/Foo/         ... Module Foo
Reply
#8

(08-31-2023, 12:36 AM)kenjis Wrote: I assumed your modules are like these:

Code:
Modules/Foo/ ... Module Foo
Modules/Bar/ ... Module Bar


But if you also use `Modules/` as one module:
Code:
Modules/ ... Main Module
You cannot remove the Module path from $psr4.

But it is not recommended. Because it is confusing.

Code:
Modules/Bar/         ... Module Bar
Modules/Config/      ... Config folder in the main Module
Modules/Controllers/ ... Controllers folder in the main Module
Modules/Foo/         ... Module Foo

Correct - each subfolder is as separate module.
I am not using "main module"


Modules/Auth
Modules/Base - this is what i can call CORE of modules, main module
Modules/Cms
Modules/Currency
Modules/Member
Modules/Settings
etc...

So Base has registrar, which connects to DB and tries to use one of Settings model.
While Base is loading, Settings module is not know by the system yet.
This makes Services misleading infinite loop.
I will jump into debuging now. 
Busy with other projects, you know...
Reply
#9

Issue solved!
As i told previously Registrar failed to laod model which is located in module which is not loaded at that time.
Services does work fine.
I had to remove module autoloading modification and manually hardcode each module i have. I tried to change order, but that did not the trick of loading sequence.

Solution: simple - hardcode database table in Registrar, instead of getting it from model. This is only case and system's languages database table name 99.9% will never change.
Reply




Theme © iAndrew 2016 - Forum software by © MyBB