What is the best method to save data in a application where you can get it afterward in any part of the application

So I have been using Zend Framework in the past before the final versions of Version 2 where I am used to use the Zend_Registry to save data like Zend_Registry::set(‘db’, $db) and get it afterwards when i needed it like Zend_Registry::get(‘db’) for other operations.

Now i know this has been taken out in the final versions of Zend and now since I wanna try out Laminas, what is the best way to save these type of data?

I have been looking at the components and the maybe the best option I have seen is to use the container from the session component? Just wanted to make sure that I would using the correct way.

The best way is not to save these types of data. Instead, you use a container to get the required component and pass it to the appropriate class.
The procedure is not a peculiarity of Laminas/Zend it is a typical way to solve this problem. (More can be found under the topic “dependency injection”.)

A concrete example can be found in the tutorials:

No sure if I understood. In the link you provided it refers to the module itself, was trying to have a wider range like at app start/run method like so for example:

Here i use the service manager to set the DB as Service where I cant get it from other files, is this correct in the older version of Zend it would be like Zend_Registry::set(‘db’, $this->db)

public function start() 
{
    $this->application = parent::init($this->config);
    
    $db_config = $this->application->getConfig()['db'];
    $db_adapter = new Adapter($db_config);
    $db_adapter->driver->getConnection()->connect();
    
    if ($db_adapter->driver->getConnection()->isConnected() === true) {
        
        $this->db = $db_adapter;
        $this->dbIsConnected = true;
        
        $this->serviceManager = $this->application->getServiceManager();
        
        $this->initSession();
        $this->initLanguage();
        
        $this->serviceManager->setService('db', $this->db);
        $this->application->run();

    }
}

If you create and register a class like the repository in one module then the access is not limited to this module. The container (service manager) in laminas-mvc is the same in the entire application. This means that you can use the repository from the blog module in any other module as well.

If you use the container then your start method is obsolete and also the concept of the registry.


Btw. if you use laminas-db in laminas-mvc then the laminas-db also be registered as module and you can access it anywhere through the container.

So just to clarify the method above is not implemented correctly and should set all modules as factories etc? In this case of the method where I start the session to save the language in session to set the multi language application how is the best case to start and save the session using the container.

Sorry if I am mixing things up just trying to learn from the documentation but progressing as I read and also interact here in the forums.

Create a listener class and register it (attach to event manager). Please have a look at the example in laminas-view:

namespace Admin;

use Admin\Listener\LayoutListener;
use Laminas\Mvc\MvcEvent;
use Laminas\View\Resolver\TemplateMapResolver;

class Module
{
    public function onBootstrap(MvcEvent $event) : void
    {
        $application = $event->getApplication();

        /** @var TemplateMapResolver $templateMapResolver */
        $templateMapResolver = $application->getServiceManager()->get(
            'ViewTemplateMapResolver'
        );

        // Create and register layout listener
        $listener = new LayoutListener($templateMapResolver);
        $listener->attach($application->getEventManager());
    }
}

https://docs.laminas.dev/laminas-view/cookbook/setting-module-specific-layouts/#register-listener

With the method $application->getServiceManager()->get(…) you can get the database adapter of your application. Inject the database adapter into the listener and perform your database queries there.

Ok just to make sure the Bootstrap here is like the init() before as it was before in Zend right? So you have onBootstrap just getting the same variables that I state on my start method.

So I could put my start method onBootstrap inside my Application Module which is the base of the application saying which is the default language there, start my session there etc also correct?

No, keep the module class and onBootstrap method clean. Only register listener or something similar. Normally in a module class not more is included than the method getConfig. More information on this topic can be found in the documentation:

“Best Practices when Creating Modules – Keep the init() and onBootstrap() methods lightweight”

The is goal should be to dissolve your start method. You can move all configuration of the session to configuration files and if you need to write the current language key in the session then use a listener.

Ok got it cause it would then run 10 times if I had 10 modules running the saem SQL to get all languages.
The thing is on my start method I have 2 other methods $this->initSession(); $this->initLanguage(); where one start the session container to say its a FrontEnd session where I would have BackEnd session to be diferente to edit from a CMS and the other method queries the database for all languages available and set into session for future use in models to query like info in the current language and also redirect the user to the language by setting the default route parameter of lang like $router->setDefaultParam(‘lang’, $this->session->lang);

Not sure how to apply this in a listener.

Try to create a listener and you will see there are no limits.

The session is not intended for this. Use the session if you need to transport an info / data value from one request to another.
For a simple language tag use PHP’s Locale class which can be set in a listener and can be retrieved later in the application.

Hey there, finally got some time to get back to this post. @froschdesign thanks for the advice.

So i created a listener like so:

namespace Application\Listener;

use Locale;
use Laminas\EventManager\AbstractListenerAggregate;
use Laminas\EventManager\EventManagerInterface;
use Laminas\Mvc\MvcEvent;

class LocaleListener extends AbstractListenerAggregate
{
    private const REGEX_LOCALE = '#^(?P<locale>[a-z]{2,3}([-_][a-zA-Z]{2}|))#';

    public function attach(EventManagerInterface $events, $priority = 1) : void
    {
        $this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH, [$this, 'setLanguage']);
    }

    public function setLanguage(MvcEvent $event) : void
    {
        $routeMatch = $event->getRouteMatch();
        
        if (!$routeMatch) {
            
            Locale::setDefault('en_US');
            return;
            
        }

        $lang = $routeMatch->getParam('lang');
        
        if (!$lang || !preg_match(self::REGEX_LOCALE, $lang, $matches)) {
            
            Locale::setDefault('en_US');
            return;
            
        }

        Locale::setDefault($matches['lang']);
    }
}

So then I would get this on the onBootstrap of the main module Application right? Where I would load into the module in by using the namespace and create a new instance from it, where from that I can get it from anywhere using the Locale::getDefault().