ZF2 service factory not injected - php

I have problems solving a catchable error:
Catchable fatal error: Argument 1 passed to
Market\Service\UserService::__construct() must be an instance of
Market\Mapper\UserMapperInterface,
In the constructor of my UserService I use UserMapperInterface (dependency injection). I created a factory class for that and I registered it in my service_manager config. Nothing is happening and an error is shown.
I created factory and services 19287319827 times but now I don't know how I can solve this noob fatal error.
Config:
'service_manager' => array(
'factories' => array(
'Market\Service\UserServiceInterface' => 'Market\Factory\UserServiceFactory',
)
),
Service:
class UserService implements UserServiceInterface
{
/**
* #var object Market\Mapper\UserMappperInteface
*/
protected $userMapper;
/**
* UserService constructor.
*
* #param UserMapperInterface $userMapperInterface
*/
public function __construct(UserMapperInterface $userMapperInterface)
{
$this->userMapper = $userMapperInterface;
}
Factory:
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Market\Service\UserService;
class UserServiceFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$userMapperInteface = $serviceLocator->get('Market\Mapper\UserMapperInterface');
return new UserService($userMapperInteface);
}
}
I can't believe that I can't solve this noob fatal error. I'm looking and looking for the last 2 hour in my code and I have been checking everything and I think everything looks good but the class is not injected in my factory.

Your problem lies in this line:
$userMapperInteface = $serviceLocator->get('Market\Mapper\UserMapperInterface');
It probably returns null or at least something else then the expected UserMapperInterface instance. Since you did not share any code on how and where this Market\Mapper\UserMapperInterface is defined or constructed it is hard to say what exactly goes wrong...
Please share more details on that part if you can't figure it out yourself.

Related

Use custom annotations in Symfony User Provider

I'm trying to make a custom User Provider which uses a custom Doctrine-like bundle. This bundles uses entities, pretty much like Doctrine does :
/**
* #EntityMeta(table="MY_TABLE")
*/
class MyTable extends AbstractEntity
{
/**
* #var int
* #EntityColumnMeta(column="Code", isKey=true)
*/
protected $code;
/**
* #var int
* #EntityColumnMeta(column="Name")
*/
protected $name;
Those annotations work well when I use the doctrine-like manager provided by my bundle. This code works well :
public function indexAction(DoctrineLikeManager $manager)
{
$lines = $manager->getRepository('MyTable')->findBy(array(
'email' => 'test#test.com'
));
// do something with these
}
So I know annotations work. But when I use the same code, with the same entity, in the User Provider Class, I get the following error :
[Semantical Error] The annotation "#NameSpace\DoctrineLikeBundle\EntityColumnMeta" in property AppBundle\MyTable::$code does not exist, or could not be auto-loaded.
The UserProvider :
class HanaUserProvider implements UserProviderInterface
{
private $manager;
public function __construct(DoctrineLikeManager $manager)
{
$this->manager = $manager;
}
public function loadUserByUsername($username)
{
// this is where it fails :(
$lines = $this->manager->getRepository('MyTable')->findBy(array(
'email' => 'test#test.com'
));
// return user or throw UsernameNotFoundException
}
}
Is it possible to use custom annotations in that context ? Maybe I should do something in particular so custom annotations can be successfully loaded ?
Thanks in advance !
Ok, I found a solution, which might not be the best but works.
The thing is that annotations don't seem to use classic autoloading, as explained here.
I had to register a loader in my User Provider :
public function loadUserByUsername($username)
{
AnnotationRegistry::registerLoader('class_exists'); // THIS LINE HERE
$lines = $this->manager->getRepository('MyTable')->findBy(array(
'email' => 'test#test.com'
));
// return user or throw UsernameNotFoundException
}
However, the problem is that this method is deprecated and will be removed in doctrine/annotations 2.0.

Symfony 3: cannot access the container from inside a controller

I'm migrating my app from Symfony 2.8 to Symfony 3.3.
From inside a controller of mine I have this:
public function indexAction()
{
$email = new Email();
$form = $this->createForm(GetStartedType::class, $email, [
'action' => $this->generateUrl('get_started_end'),
'method' => 'POST',
]);
return [
'form' => $form->createView(),
];
}
But I receive this exception:
Call to a member function get() on null
My controller extends Symfony\Bundle\FrameworkBundle\Controller\Controller:
/**
* {#inheritdoc}
*/
class DefaultController extends Controller
{
...
}
So I have access to the container.
Putting some dumps around in the Symfony's code, I see that the container is correctly set:
namespace Symfony\Component\DependencyInjection;
/**
* ContainerAware trait.
*
* #author Fabien Potencier <fabien#symfony.com>
*/
trait ContainerAwareTrait
{
/**
* #var ContainerInterface
*/
protected $container;
/**
* Sets the container.
*
* #param ContainerInterface|null $container A ContainerInterface instance or null
*/
public function setContainer(ContainerInterface $container = null)
{
dump('Here in the ContainerAwareTrait');
dump(null === $container);
$this->container = $container;
}
}
This dumps
Here in the ContainerAwareTrait
false
So the autowiring works well and sets the container.
But in the ControllerTrait I have this:
trait ControllerTrait
{
/**
* Generates a URL from the given parameters.
*
* #param string $route The name of the route
* #param mixed $parameters An array of parameters
* #param int $referenceType The type of reference (one of the constants in UrlGeneratorInterface)
*
* #return string The generated URL
*
* #see UrlGeneratorInterface
*/
protected function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH)
{
dump('Here in the ControllerTrait');
die(dump(null === $this->container));
return $this->container->get('router')->generate($route, $parameters, $referenceType);
}
...
this is the dump:
Here in the ControllerTrait
true
So here the container is null and this causes the error.
Anyone can help me solve this issue?
Why is the container null?
If may help, this is the services.yml configuration (the default that cames with Symfony):
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
This question is posted as issue on the Symfony's issue tracker.
The S3.3 autowire capability makes it a bit easier to define controllers as services.
The usual motivation behind defining controllers as services is to avoid injecting the container. In other words you should be explicitly injecting each service a controller uses. The autowire capability allows you to use action method injection so you don't have to inject a bunch of stuff in the constructor.
However, the base Symfony controller class provides a number of helper function which use about 12 different services. It would be painful indeed to inject these one at a time. I had sort of thought that the autowire capability might take care of this for you but I guess not.
So you basically need to add a call to setContainer in your service definition. Something like:
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
[[setContainer, ['#service_container']]]
tags: ['controller.service_arguments']
The autowire capability is very much a work in progress so I would not be surprised if this changes for 3.4/4.0.
This problem is fixed by PR #23239 and is relased in Symfony 3.3.3.

PHPDoc return type with extended Factory pattern

I want to use an abstract base class with common functionality for factories to extend, which works, but I don't know how to accurately specify the return type and have it detected by PHPStorm.
Here's an example. Is there a way I can document in PHPDoc that AppleFactory::make() returns AppleInterface and OrangeFactory::make() returns OrangeInterface?
<?php
namespace App\Factories;
abstract class AbstractFactory {
/** #var array $drivers */
protected $drivers;
/**
* instantiate the driver based on the given driver identifier
* #param string $driver Driver identifier.
* #return ???
* #throws UnknownDriverException If driver string is not in list of available drivers.
*/
public function make($driver) {
$class = $this->className($driver);
if (is_null($class))
throw new UnknownDriverException($driver);
return new $class;
}
/**
* get the full class name for the driver
* #param string $driver String mapping of class.
* #return string
*/
public function className($driver) {
return isset($this->drivers[$driver]) ? $this->drivers[$driver] : null;
}
}
class AppleFactory extends AbstractFactory {
protected $drivers = [
// both implement AppleInterface
'fuji' => \App\Apples\Fuji::class,
'gala' => \App\Apples\Gala::class
];
}
class OrangeFactory extends AbstractFactory {
protected $drivers = [
// both implement OrangeInterface
'navel' => \App\Oranges\Navel::class,
'blood' => \App\Oranges\Blood::class
];
}
Is there a way I can document in PHPDoc that AppleFactory::make() returns AppleInterface and OrangeFactory::make() returns OrangeInterface?
Based on your requirements above - a standard #method should do the job -- needs to be placed in PHPDoc comment for that class (AppleFactory and OrangeFactory accordingly). Something like this:
#method AppleInterface make($driver)
At the same time, since you do pass parameter to a factory method .. and returned instance has strong relation to that -- have a look at Advanced Metadata support in PhpStorm (IDE specific functionality). This is what Laravel IDE helper (for example) uses to provide better IDE integration with this framework.
More on this: https://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Advanced+Metadata

Laravel class Auth

Hi can I ask about this in laravel framework
namespace Illuminate\Support\Facades;
/**
* #see \Illuminate\Auth\AuthManager
* #see \Illuminate\Contracts\Auth\Factory
* #see \Illuminate\Contracts\Auth\Guard
* #see \Illuminate\Contracts\Auth\StatefulGuard
*/
class Auth extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'auth';
}
}
what does the return 'auth' exactly returning to the caller ? is it text 'auth' or an object ? and what is the reason why they only have one method in that class ? I apologize i am just learning oop.
Thank you in advance.
In this case as you see method getFacadeAccessor it's returning auth string.
Facades are just "shortcuts" to use other classes but in fact you shouldn't use them everywhere if you don't need to.
In Laravel you can bind objects/classes into Application. So you can write for example:
$app->bind('something', function() {
return new SomeObject();
});
Let's assume there is method doSomething in SomeObject class.
Now you can use this method using:
$app['something']->doSomething();
But you can also create facade:
class GreatClass extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'something';
}
}
and now anywhere in your application you could use:
GreatClass::doSomething();
Answering your question this getFacadeAccessor is returning only the name the name of object that is used when bound to Application. To know how it's used you can look into the source of:
/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
The method you should look first is getFacadeRoot - because this method is returning the requested object.

ZF2 domain specific object

i have to fetch an entity object, based on the given domain of my application. Based on that entity, other services handle their requests.
Is there a way to store a static object or something like that? Or should i inject it into the other services, but where should i store it that way?
Thanks for any help
-- EDIT --
Now i have the following Service factory to return the wantend entity. But now i have the problem, that in case of the default domain, no entity exists and my application crashes because I can't return a null value in the factory. How can I solve that?
namespace Client\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ClientFactory implements FactoryInterface {
/**
* #param \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator
* #return \Client\Entity\Client
*/
public function createService(ServiceLocatorInterface $serviceLocator) {
/* #var $clientService \Client\Service\ClientServiceInterface */
$clientService = $serviceLocator->get('Client\Service\Client');
$baseUrl = $serviceLocator->get('Request')->getUri()->getHost();
// client.mydomain.com returns a client entity
// www.mydomain.com doesn't return a client entity
return $clientService->getClientByBaseUrl($baseUrl);
}
}

Categories