zend framework 2 getServiceLocator Empty - php

I've been trying to figure it out by myself reading online and applying tons of answers form here, but to no avail unfortunately.
I have two Modules on my zf2 application, one called Services and one called Agent.
Now, in my Services module everything seems to work fine, I can get my serviceLocator, hence my configuration, and work with it. In my Agent Module's controller however, I don't seem to be able to do the same.
This is part of my AgentController:
use Zend\Mvc\Controller\AbstractActionController;
class AgentController extends AbstractActionController
{
protected $serviceLocator;
public function ValidateAction()
{
$this->serviceLocator = $this->getServiceLocator()->get('config');
//... Using the config
}
}
In my module.cofig.php I have the following:
'controllers' => array(
'invokables' => array(
'Agent\Controller\Agent' => 'Agent\Controller\AgentController',
),
),
I have tried many solutions: changing and adding methods to the Module.php, changing the module.config etc.. Where am I wrong?
Thanks,
Andrea

The class variable $this->serviceLocator is used by the controller class to hold the service locator instance. In your example you assigning the config array to this variable (thus replacing the service locator instance with an array). Subsequent calls to $this->getServiceLocator() will then return the config array instead of the service locator object, which is the likely cause of the error you're getting.
I'd suggest either using a local variable instead:
public function ValidateAction()
{
$config = $this->getServiceLocator()->get('config');
//... Using the config
}
or assigning to a class variable with a different name:
public function ValidateAction()
{
$this->config = $this->getServiceLocator()->get('config');
//... Using the config
}

Related

symfony router library not find controller

I have This folder structure for my mvc project:
application
---admin
--------controller
--------model
--------view
--------language
---front
--------controller
------------------BlogController.php
--------model
--------view
--------language
core
public
I work With symfony router library and route my url like this:
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$routes = new RouteCollection();
$routes->add('blog_list', new Route('/blog', array(
'_controller' => [application\front\controller\BlogController::class, 'index']
)));
return $routes;
BlogController.php is:
namespace application\front\controller;
class BlogController extends Controller
{
/**
* Construct this object by extending the basic Controller class
*/
public function __construct()
{
parent::__construct();
}
public function index()
{
echo 'fine';
}
}
But In Action I can't see Any output. I think syfony can find my controller. Can I fix this problem ?!
You didn't define which symfony version you are using. But checking from similar example on https://symfony.com/doc/current/routing/custom_route_loader.html for latest one (4.1), it looks like the controller value should be a string instead of an array (I haven't needed to use this kind of 'manual' route definition myself so I don't really know whether the constructor supports several different ways of defining the controller value - meaning that this is more of an educated guess what your issue could be :)).
$routes->add('blog_list', new Route('/blog', array(
'_controller' => 'application\front\controller\BlogController::index'
)));

ZF2 Model used in many modules using zend global config

I have a Class object which is used in many modules in my zend structure :
/module/
--|Aplication
--|MyClassModule
----|config
----|src
------|Factory
------|Model
---------|> MyObjectClass.php
----Module.php
--|AnotherModule
So my idea is to use this MyObjectClass.php in other modules so I can avoid duplication and have its own configuration. So far, for this is ok, however I want to get the variables set from my config/autoload files injected in this class but I don't know how.
How can I load this config data into my class model? Which is the best approach ? I can load it by accessing this directly but I don't think this is very elegant
e.g: $configArray = require './config/autoload/config.local.php';
I am not very experienced with zend so I dont know where to start with. I have seen many tutorials of how to do this via controllers, views.. etc but not in specific classes.
Thank you.
All config files are merged into one config, when your ZF2 application is bootstrapped. That includes local.php, global.php from config/autoload and all used modules' module.config.php. With a bit of more research, you can overwrite the standard loading, e.g. loading custom configs.
After bootstrapping, your are able to access the config from the ServiceManager. There are preserved keys for some ZF2-specific configs, service_manager, etc.
$serviceManager->get('config');
There is a "standard" service pattern in ZF2: Factory. This can be applied for Controllers, Services. What ever you want.
namespace Application\Factory;
use Application\Model\MyObjectClass;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyObjectFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
// get some config parameter, inject it into model
$config = $serviceLocator->get('config');
$myObjectClass = new MyObjectClass();
// ... e.g. $myObjectClass->setConfig($config);
return $myObjectClass;
}
}
It should be clear, what this factory is made for: create and return an instance of your custom object ;) You may configure your instance with some config params. With ServiceLocator as method param, you are able to access the config, other services etc.
Further, you have to register your own service/factory in the factories section of service_manager config in your module's module.config.php:
return array(
'service_manager' => array(
'factories' => array(
'MyObjectFactory' => 'Application\Factory\MyObjectFactory',
),
),
);
Now you should be able to access your factory, e.g. in an ActionController or wherever you have access to ServiceManager. That means, you can also access this factory from different modules.
public function someCustomAction() {
$myObjectClass = $this->getServiceLocator()->get('MyObjectFactory');
$myObjectClass2 = $this->getServiceLocator()->get('MyObjectFactory');
var_dump($myObjectClass);
var_dump($myObjectClass2);
if ($myObjectClass === $myObjectClass2) {
echo '<br />equal';
}
$myObjectClass = new MyObjectClass();
$myObjectClass2 = new MyObjectClass();
var_dump($myObjectClass);
var_dump($myObjectClass2);
}
Note:
Be aware, that ServiceManager returns the same instance of your object. So, that seems like what you ask for? In contrast, creating a new instance will create different objects.
Note 2:
Tested with ZF2 v2.4.9

ZF2 - How do I get application config items within my model?

In my job I am dealing with a legacy app running on ZF2. There is a model which is sending out a variety of different emails to difference addresses. The one thing they have in common is they all need to BCC to one particular address.
At first, in my head I was cursing the previous developer, for foolishly hard coding the email address 20 different times in one file. I assumed that it would be a piece of cake to grab an application config with a simple call $this->config->get('x') (like in Laravel) or something along them lines. Now I find myself feeling bad, because I understand why the previous dev did hard code the email addresses.
So to the question, how the hell do I grab a config item from application.config.php inside the model? I keep reading about how I need to implement the ServiceLocaterAware Interface. Is this really necessary? There must be a way to grab configs easily, surely?!?
How the hell do I grab a config item from application.config.php inside the model?
You shouldn't do so inside, do it 'outside'.
Register your model class as a service in module.config.php.
'service_manager' => [
'factories' => [
'Email\Model\EmailModel' => 'Email\Model\EmailModelFactory',
]
],
Then create the factory Email\Model\EmailModelFactory, this uses the service manager to fetch the 'email' config key and injects it into the model's constructor.
namespace Email\Model;
use Email\Model\EmailModel;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
class EmailModelFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new EmailModel($this->getEmailOptions($serviceLocator));
}
// Return the email options
public function getEmailOptions(ServiceLocatorInterface $serviceLocator)
{
$options = $serviceLocator->get('config');
return $options['email'];
}
}
The issue you will now have is all calls to your model classes will have to use $serviceManager->get('Email\Model\EmailModel') (rather than new \Email\Model\EmailModel) in order for the configuration to be injected. Even without seeing any of your legacy application my guess is that this would be difficult.
The model should not be responsible for sending emails; you could replace it with an service class, e.g. 'EmailService' and repeat the injection example above just for this class.
EmailService::send(EmailModel $email, array $options);
This would keep your model independent and there would be no need to replace the calls to new Model etc.
You need the service locator / service manager
Within your controller:
public function xxxAction()
{
$sm = $this->getServiceLocator();
$config = $sm->get('config');
}

Laravel: Difference between Facades and Aliases

Good day!
The more I read, the more I get confused about this. What is the difference between a Facade and Aliases?
I have this Class:
/app/libraries/Project/Data.php
namespace PJ;
class Data {
// It is much like a data container, with static methods and properties for saving info
}
And the corresponding facade, so I can access by using just PJD:: .
According to some webpage around:
... Laravel Facades are proxies. They wrap around and call functions
on the underlying true implementation of the code. Further, in the
context of a Laravel application, these Facades are accessed by
assigning them to aliases. This use of the Dependency Injection
container allow you to reference something like
Illuminate\Support\Facades\Filesystem by simply calling File.
(http://ryantablada.com/post/proxies-service-locators-alias-facades-and-war)
But, I've also found and successfully tested that adding something like:
__app/config/app.php__
'aliases' => array(
//....,
'PJD' => 'PJ\Data',
),
I can also access my class the same way.
So, what's the difference?
Thanks
EDIT #01
I have created a class named Data in /app/libraries/Project/Data.php
namespace PJ;
class Data {
// It is much like a data container, with static methods and properties for saving info
}
I have a Facade Class for this Class Data /app/libraries/Project/DataFacade.php
use Illuminate\Support\Facades\Facade;
class PJD extends Facade {
protected static function getFacadeAccessor() {
return 'PJData';
}
}
And I have a Service Provider for them: /app/libraries/Project/DataServiceProvider.php
use Illuminate\Support\ServiceProvider;
class DataServiceProvider extends ServiceProvider {
public function register() {
$this->app->singleton('PJData', function() {
return new PJ\Data;
});
}
}
I also have added to /app/config/app.php:
'providers' => array(
// ....
'DataServiceProvider',
),
and in composer.json I've added a psr-4 line to direct PJ namespace to /app/libraries/Project
"psr-4": {
"PJ\\": "app/libraries/Project"
},
By doing all this, I can access my class from anywhere in the project just by PJD:: instead of PJ\Data::.
However, I've also noticed that just by adding to /app/config/app.php
'aliases' => array(
//....,
'PJD' => 'PJ\Data',
),
I get exactly the same result without all that facades and ServiceProviders. So, what's the point of one or another?
Thanks, and sorry for the large post.
Facade and Alias are two totally different concepts.
you can not access PJ\Data\ by PJD:: unless you have setup alias in the service provider while binding.
If you are accessing it, without defining it in config/app.php, then you have set it up in the service provider file itself.
Definition of alias,
used to indicate that a named person is also known or more familiar under another specified name.
It simply means you are giving a different name to the class so that it will be easier to call.
e.g.
if you have a class like this: Foo\Bar\AVeryLongNamespaceClassName\Data, you can just give an alias, (e.g. PJD) and access its methods and properties by this alias.
Note:
Unit testing is an important aspect of why facades work the way that they do. In fact, testability is the primary reason for facades to even exist.

Can I use namespaced models with FactoryMuff?

I'm working on a new Laravel 4 project which I'm developing with a test-driven approach. I was able to test models like a boss just fine when all of my models were in the global namespace. However, I like to namespace stuff. I added namespaces to all of my models and controllers, but now when I run phpunit, I'm getting ErrorException: User Model is not an valid Class for FactoryMuff.
I've tried specifying the namespaces in the $factory variable as defined in my model, for example:
public static $factory = array(
    'title' => 'string',
    'slug' => 'string',
    'content' => 'text',
    'author_id' => 'factory|\\Project\\Model\\User',
);
However I still get the same error. I've also tried specifying the namespaces in the actual test case itself, for example:
$user = FactoryMuff::create('\\Project\\Model\\User');
I still get the same error.
I've also attemted to add use statements to the tests and models to make sure that the appropriate models are available within the scope of the tests, but that didn't yield any results either.
Question
Is it possible to use FactoryMuff with namespaced models; and if so, how?
YES, it is possible to use namespaced models with FactoryMuff.
In addition to declaring the fully namespaced class in the $factory variable within the model and the FactoryMuff::create() argument, you must also use the full namespace of the model in the relationship declaration. For example:
public function author()
{
return $this->hasMany('Project\\Model\\Post');
}
And in the related model:
public function post()
{
return $this->belongsTo('Project\\Model\\User');
}

Categories