Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Container not injected in controllers with FosRest 2.2 and Symfony 3.3 in rest routes #1719

Closed
empathie opened this issue Jun 14, 2017 · 24 comments

Comments

@empathie
Copy link

When trying to set up a route for my rest api, when I visit my "/places" route I get a FatalThrowableError: "Call to a member function has() on null" in vendor\symfony\symfony\src\Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait.php (line 356)

The problem only happens when I switch my routes from annotation to rest.

This doesn't work:

places:
    type:     rest
    resource: AppBundle\Controller\PlaceController

This works:

places:
    type:     annotation
    resource: AppBundle\Controller\PlaceController

The method is almost empty, only a call to getDoctrine:

    /**
     * @Get("/places")
     */
    public function getPlacesAction(Request $request)
    {
        dump($this->container);
        $this->getDoctrine();

        return new JsonResponse($formatted);
    }

And the container is null. I checked and the setContainer method is never called on my Controller and I can't find why.

The same thing works with identical configuration with Symfony 3.2.9, but not with Symfony 3.3.

@xabbuh
Copy link
Member

xabbuh commented Jun 15, 2017

Are you able to set up a small example project that makes it possible to reproduce your issue?

@maxixcom
Copy link

I have the same problem with Symfony 3.3.2. $this->container is null. So I get:
"Error: Call to a member function has() on null" when I invoke $this->getDoctrine() in controller.

@xabbuh
Copy link
Member

xabbuh commented Jun 19, 2017

Maybe related to symfony/symfony#23200?

Do you make use of the new feature that makes it possible to easily register controllers as services?

@maxixcom
Copy link

Yes it might be related.
I've tried this solution for my controller(service.yml) as was mentioned in symfony/symfony#23200 thread:

calls:
        - [setContainer, ["@service_container"]]

and it works. But it's obvious that something wrong with auto injecting container in controller.

@xabbuh
Copy link
Member

xabbuh commented Jun 19, 2017

Do you use JMSDiExtraBundle?

@maxixcom
Copy link

I don't. Here is my composer.json deps:

{
    "name": "root/api",
    "license": "proprietary",
    "type": "project",
    "autoload": {
        "psr-4": {
            "": "src/"
        },
        "classmap": [
            "app/AppKernel.php",
            "app/AppCache.php"
        ]
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        },
        "files": [
            "vendor/symfony/symfony/src/Symfony/Component/VarDumper/Resources/functions/dump.php"
        ]
    },
    "require": {
        "php": ">=5.5.9",
        "doctrine/doctrine-bundle": "^1.6",
        "doctrine/doctrine-migrations-bundle": "^1.2",
        "doctrine/orm": "^2.5",
        "friendsofsymfony/user-bundle": "~2.0",
        "incenteev/composer-parameter-handler": "^2.0",
        "lexik/jwt-authentication-bundle": "^2.4",
        "sensio/distribution-bundle": "^5.0.19",
        "sensio/framework-extra-bundle": "^3.0.2",
        "symfony/monolog-bundle": "^3.1.0",
        "symfony/polyfill-apcu": "^1.0",
        "symfony/swiftmailer-bundle": "^2.3.10",
        "symfony/symfony": "3.3.*",
        "twig/twig": "^1.0||^2.0",

        "nelmio/cors-bundle": "^1.4",
        "nelmio/api-doc-bundle": "^2.11",
        "friendsofsymfony/rest-bundle": "^2.1",
        "jms/serializer-bundle": "^1.1"
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0",
        "symfony/phpunit-bridge": "^3.0"
    },
    "scripts": {
        "symfony-scripts": [
            "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget"
        ],
        "post-install-cmd": [
            "@symfony-scripts"
        ],
        "post-update-cmd": [
            "@symfony-scripts"
        ]
    },
    "config": {
        "sort-packages": true
    },
    "extra": {
        "symfony-app-dir": "app",
        "symfony-bin-dir": "bin",
        "symfony-var-dir": "var",
        "symfony-web-dir": "web",
        "symfony-tests-dir": "tests",
        "symfony-assets-install": "relative",
        "incenteev-parameters": {
            "file": "app/config/parameters.yml"
        },
        "branch-alias": null
    }
}

@xabbuh
Copy link
Member

xabbuh commented Jun 19, 2017

Do you register your controllers as services? If yes, what does your config look like? Can you also show the code of your controller?

@maxixcom
Copy link

This is the controller I stuck with:

namespace AppBundle\Controller;

use FOS\RestBundle\Controller\Annotations\RouteResource;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Routing\ClassResourceInterface;

/**
 * Class UserController
 * @package AppBundle\Controller
 *
 * @RouteResource("user")
 */
class UserController extends FOSRestController implements ClassResourceInterface
{
    public function getAction($id) {
        var_dump($this->container); // display null
        exit;

        // next code gives error because container is null:

//        return $this->getDoctrine()
//            ->getRepository('AppBundle:User')
//            ->find($id);
    } // "get_user"      [GET] /users/{slug}
}

My services.yml:

# Learn more about services, parameters and containers at
# http://symfony.com/doc/current/service_container.html
parameters:
    #parameter_name: value

services:
    # default configuration for services in *this* file
    _defaults:
        # automatically injects dependencies in your services
        autowire: true
        # automatically registers your services as commands, event subscribers, etc.
        autoconfigure: true
        # this means you cannot fetch services directly from the container via $container->get()
        # if you need to do this, you can override this setting on individual services
        public: false

    # makes classes in src/AppBundle available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    AppBundle\:
        resource: '../../src/AppBundle/*'
        # you can exclude directories or files
        # but if a service is unused, it's removed anyway
        exclude: '../../src/AppBundle/{Entity,Repository}'

    # 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']

    # add more services, or override services that need manual wiring
    # AppBundle\Service\ExampleService:
    #     arguments:
    #         $someArgument: 'some_value'


# ----------------------------------------------------------------
# !!!!!!! Next code inject container and the code is start working
#    AppBundle\Controller\UserController:
#        calls:
#            - [setContainer, ["@service_container"]]

@nusje2000
Copy link

I got the same issue, it is a pain in the ass

@xabbuh
Copy link
Member

xabbuh commented Jun 20, 2017

Can someone of you create a small example project that makes it easily possible to reproduce your issue?

@maxixcom
Copy link

@xabbuh Here is a test project with the problem:
https://github.com/maxixcom/symfony-container

But it use db. You need configure and create some db. And run migrations:

./bin/console doctrine:migration:migrate

Actually it creates FOSUserBundle User table.
Look at UserController::getAction - (GET request - /users/1). There is a problem.
But if you try DefaultController::indexAction (GET request - /) you'll see that container exist

@xabbuh
Copy link
Member

xabbuh commented Jun 20, 2017

Thank you for providing this useful example. I think I was able to reproduce the issue. Can you please check if symfony/symfony#23239 fixes it for you?

@maxixcom
Copy link

@xabbuh Yes, it fixes the issue. Container is in controller now.

@thibaultfalque
Copy link

Hi,
I have the same issue. How to fix the problem? Manually?

Thanks.

@fpilee
Copy link

fpilee commented Jun 26, 2017

to fix it you have to update Framework Bundle... or just edit it and add the new content

https://github.com/symfony/symfony/pull/23239/files

vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php

@secit-pl
Copy link

Will it be fixed somehow in the bundle or should we wait for the next symfony release and until then apply some custom fixes?

nicolas-grekas added a commit to symfony/symfony that referenced this issue Jul 3, 2017
…llers (xabbuh)

This PR was merged into the 3.3 branch.

Discussion
----------

[FrameworkBundle] call setContainer() for autowired controllers

| Q             | A
| ------------- | ---
| Branch?       | 3.3
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #23200, FriendsOfSymfony/FOSRestBundle#1719
| License       | MIT
| Doc PR        |

Previously, you either did not use controllers as services or you explicitly wired everything yourself. In case of a non-service controller the FrameworkBundle took care of calling `setContainer()` inside the `instantiateController()` method:

```php
protected function instantiateController($class)
{
    $controller = parent::instantiateController($class);

    if ($controller instanceof ContainerAwareInterface) {
        $controller->setContainer($this->container);
    }
    if ($controller instanceof AbstractController && null !== $previousContainer = $controller->setContainer($this->container)) {
        $controller->setContainer($previousContainer);
    }

    return $controller;
}
```

With the new autowired controllers as services this is no longer happening as controllers do not need to be instantiated anymore (the container already returns fully built objects).

Commits
-------

1d07a28 call setContainer() for autowired controllers
@nicolas-grekas
Copy link

symfony/symfony#23239 now merged

symfony-splitter pushed a commit to symfony/framework-bundle that referenced this issue Jul 3, 2017
…llers (xabbuh)

This PR was merged into the 3.3 branch.

Discussion
----------

[FrameworkBundle] call setContainer() for autowired controllers

| Q             | A
| ------------- | ---
| Branch?       | 3.3
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #23200, FriendsOfSymfony/FOSRestBundle#1719
| License       | MIT
| Doc PR        |

Previously, you either did not use controllers as services or you explicitly wired everything yourself. In case of a non-service controller the FrameworkBundle took care of calling `setContainer()` inside the `instantiateController()` method:

```php
protected function instantiateController($class)
{
    $controller = parent::instantiateController($class);

    if ($controller instanceof ContainerAwareInterface) {
        $controller->setContainer($this->container);
    }
    if ($controller instanceof AbstractController && null !== $previousContainer = $controller->setContainer($this->container)) {
        $controller->setContainer($previousContainer);
    }

    return $controller;
}
```

With the new autowired controllers as services this is no longer happening as controllers do not need to be instantiated anymore (the container already returns fully built objects).

Commits
-------

1d07a28 call setContainer() for autowired controllers
@xabbuh xabbuh closed this as completed Jul 3, 2017
@xabbuh
Copy link
Member

xabbuh commented Jul 4, 2017

Symfony 3.3.3 containing this fix was released.

@rileyrg
Copy link

rileyrg commented Sep 29, 2017

Symfony 3.3.9 has this issue when using JMSDiExtraBundle. The "solution" (I bracket it as I have NO idea whats going on) was to add the setContainer call to my controllers in the "generic" autowiring section:

# 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']
    **calls:
       - [setContainer, ["@service_container"]]**

@ventuc
Copy link

ventuc commented Nov 21, 2017

Also Symfony 3.3.13 is still affected. The solution is the same above... but didn't have it been fixed already?

@xabbuh
Copy link
Member

xabbuh commented Nov 21, 2017

Are you using JMSDiExtraBundle?

@ventuc
Copy link

ventuc commented Nov 21, 2017

Yes, version 1.9.0

@xabbuh
Copy link
Member

xabbuh commented Nov 21, 2017

Then it's probably related to their code. I don't know if they ever fixed the issue on their side.

@egorzot
Copy link

egorzot commented Feb 19, 2018

@ventuc you need to downgrade JMSDiExtraBundle to 1.7.0 version.
It solves the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests