sonata-project / SonataSeoBundle

Symfony SonataSeoBundle
https://docs.sonata-project.org/projects/SonataSeoBundle
MIT License
136 stars 90 forks source link

Service "sonata.seo.page" not found #310

Closed digitalray closed 5 years ago

digitalray commented 5 years ago

Environment

dev macOS Mojave v10.14.1

Sonata packages

$ composer show --latest 'sonata-project/*'
sonata-project/exporter   1.9.1 1.9.1 Lightweight Exporter library
sonata-project/seo-bundle 2.6.2 2.6.2 Symfony SonataSeoBundle

Symfony packages

$ composer show --latest 'symfony/*'
Restricting packages listed in "symfony/symfony" to "4.1.*"
symfony/asset                 v4.1.7  v4.1.9  Symfony Asset Component
symfony/browser-kit           v4.1.7  v4.1.9  Symfony BrowserKit Component
symfony/cache                 v4.1.7  v4.1.9  Symfony Cache component with PSR-6, PSR-16, and tags
symfony/config                v4.1.7  v4.1.9  Symfony Config Component
symfony/console               v4.1.7  v4.1.9  Symfony Console Component
symfony/css-selector          v4.1.7  v4.1.9  Symfony CssSelector Component
symfony/debug                 v4.1.7  v4.1.9  Symfony Debug Component
symfony/debug-bundle          v4.1.7  v4.1.9  Symfony DebugBundle
symfony/debug-pack            v1.0.6  v1.0.7  A debug pack for Symfony projects
symfony/dependency-injection  v4.1.7  v4.1.9  Symfony DependencyInjection Component
symfony/doctrine-bridge       v4.1.7  v4.1.9  Symfony Doctrine Bridge
symfony/dom-crawler           v4.1.7  v4.1.9  Symfony DomCrawler Component
symfony/dotenv                v4.1.7  v4.1.9  Registers environment variables from a .env file
symfony/event-dispatcher      v4.1.7  v4.1.9  Symfony EventDispatcher Component
symfony/expression-language   v4.1.7  v4.1.9  Symfony ExpressionLanguage Component
symfony/filesystem            v4.1.7  v4.1.9  Symfony Filesystem Component
symfony/finder                v4.1.7  v4.1.9  Symfony Finder Component
symfony/flex                  v1.1.8  v1.1.8  Composer plugin for Symfony
symfony/form                  v4.1.8  v4.1.9  Symfony Form Component
symfony/framework-bundle      v4.1.7  v4.1.9  Symfony FrameworkBundle
symfony/http-foundation       v4.1.7  v4.1.9  Symfony HttpFoundation Component
symfony/http-kernel           v4.1.7  v4.1.9  Symfony HttpKernel Component
symfony/inflector             v4.1.7  v4.1.9  Symfony Inflector Component
symfony/intl                  v4.1.7  v4.1.9  A PHP replacement layer for the C intl extension that includes additional data from the ICU library.
symfony/maker-bundle          v1.9.0  v1.9.0  Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.
symfony/monolog-bridge        v4.1.7  v4.1.9  Symfony Monolog Bridge
symfony/monolog-bundle        v3.3.1  v3.3.1  Symfony MonologBundle
symfony/options-resolver      v4.1.7  v4.1.9  Symfony OptionsResolver Component
symfony/orm-pack              v1.0.5  v1.0.5  A pack for the Doctrine ORM
symfony/panther               v0.2.0  v0.2.0  A browser testing and web scraping library for PHP and Symfony.
symfony/phpunit-bridge        v4.1.7  v4.2.1  Symfony PHPUnit Bridge
symfony/polyfill-intl-icu     v1.10.0 v1.10.0 Symfony polyfill for intl's ICU-related data and classes
symfony/polyfill-mbstring     v1.10.0 v1.10.0 Symfony polyfill for the Mbstring extension
symfony/polyfill-php72        v1.10.0 v1.10.0 Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions
symfony/process               v4.1.7  v4.1.9  Symfony Process Component
symfony/profiler-pack         v1.0.3  v1.0.4  A pack for the Symfony web profiler
symfony/property-access       v4.1.7  v4.1.9  Symfony PropertyAccess Component
symfony/property-info         v4.1.7  v4.1.9  Symfony Property Info Component
symfony/routing               v4.1.7  v4.1.9  Symfony Routing Component
symfony/security              v4.1.7  v4.1.9  Symfony Security Component
symfony/security-bundle       v4.1.7  v4.1.9  Symfony SecurityBundle
symfony/serializer            v4.1.7  v4.1.9  Symfony Serializer Component
symfony/serializer-pack       v1.0.1  v1.0.2  A pack for the Symfony serializer
symfony/stopwatch             v4.1.7  v4.1.9  Symfony Stopwatch Component
symfony/swiftmailer-bundle    v3.2.4  v3.2.4  Symfony SwiftmailerBundle
symfony/test-pack             v1.0.4  v1.0.5  A pack for functional and end-to-end testing within a Symfony app
symfony/translation           v4.1.7  v4.1.9  Symfony Translation Component
symfony/twig-bridge           v4.1.7  v4.1.9  Symfony Twig Bridge
symfony/twig-bundle           v4.1.7  v4.1.9  Symfony TwigBundle
symfony/validator             v4.1.7  v4.1.9  Symfony Validator Component
symfony/var-dumper            v4.1.7  v4.1.9  Symfony mechanism for exploring and dumping PHP variables
symfony/web-link              v4.1.7  v4.1.9  Symfony WebLink Component
symfony/web-profiler-bundle   v4.1.7  v4.1.9  Symfony WebProfilerBundle
symfony/web-server-bundle     v4.1.9  v4.1.9  Symfony WebServerBundle
symfony/webpack-encore-bundle v1.0.0  v1.0.0  Integration with your Symfony app & Webpack Encore!
symfony/yaml                  v4.1.7  v4.1.9  Symfony Yaml Component

PHP version

$ php -v
PHP 7.2.11 (cli) (built: Oct 11 2018 16:24:11) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.11, Copyright (c) 1999-2018, by Zend Technologies

Subject

When configuring sonata.seo.page with latest symfony project, I get a service not found error.

Steps to reproduce

composer create-project symfony/skeleton demo
composer require composer require sonata-project/seo-bundle

Create sonata_seo.yaml in demo/config/packages

sonata_seo:
    encoding:         UTF-8
    page:
        title:            Project name

Create base template: demo/templates/base.html.twig

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        {% block title %}{% endblock %}
  </head>
  <body>
        {% block body %}
        {% endblock %}
  </body>
</html>

Create template: demo/templates/default/index.html.twig

{% extends 'base.html.twig' %}
{% block title %}
    {{ sonata_seo_title() }}
{% endblock %}

{% block body %}
<div><p>Test</p></div>
{% endblock %}

Create a function in demo/src/DefaultController.php like this

    public function index()
    {

        $seoPage = $this->container->get('sonata.seo.page');
        $seoPage->setTitle('some title');
        return $this->render('default/index.html.twig', [
            'controller_name' => 'DefaultController',
        ]);
    }

Add route to demo/config/routes.yaml

index:
    path: /
    controller: App\Controller\DefaultController::index

Expected results

Page should render with title set to "some title"

Actual results

Service "sonata.seo.page" not found: even though it exists in the app's container, the container inside "App\Controller\DefaultController" is a smaller service locator that only knows about the "doctrine", "form.factory", "http_kernel", "parameter_bag", "request_stack", "router", "security.authorization_checker", "security.csrf.token_manager", "security.token_storage", "serializer", "session" and "twig" services. Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "DefaultController::getSubscribedServices()".

Stack Trace

Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException:
Service "sonata.seo.page" not found: even though it exists in the app's container, the container inside "App\Controller\DefaultController" is a smaller service locator that only knows about the "doctrine", "form.factory", "http_kernel", "parameter_bag", "request_stack", "router", "security.authorization_checker", "security.csrf.token_manager", "security.token_storage", "serializer", "session" and "twig" services. Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "DefaultController::getSubscribedServices()".

  at vendor/symfony/dependency-injection/ServiceLocator.php:51
  at Symfony\Component\DependencyInjection\ServiceLocator->get('sonata.seo.page')
     (src/Controller/DefaultController.php:18)
  at App\Controller\DefaultController->index()
     (vendor/symfony/http-kernel/HttpKernel.php:149)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
     (vendor/symfony/http-kernel/HttpKernel.php:66)
  at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
     (vendor/symfony/http-kernel/Kernel.php:188)
  at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
     (public/index.php:37)
kunicmarko20 commented 5 years ago

The error seems clear to me. This is not a Sonata issue, but how you fetch the service in your application. Closing.

OskarStark commented 5 years ago

You mean instead of inject it in the action? Public/private?

digitalray commented 5 years ago

I’ve followed documentation and used the example in the doc in my code. If you don’t mind me asking, what is the correct way to fetch it?

kunicmarko20 commented 5 years ago

Injecting it into the constructor of your controller would be the best option. Docs are outdated.

digitalray commented 5 years ago

Thanks @kunicmarko20. When I added it in constructor I got a different error. constructor

public $seoPage;

    public function __construct()
    {
        $this->seoPage = $this->container->get('sonata.seo.page');
    }

Error Call to a member function get() on null

Error:
Call to a member function get() on null

  at src/Controller/DefaultController.php:18
  at App\Controller\DefaultController->__construct()
     (var/cache/dev/ContainerOZovzCR/getDefaultControllerService.php:13)
  at require('/Users/schakarian/repos/dr-web/var/cache/dev/ContainerOZovzCR/getDefaultControllerService.php')
     (var/cache/dev/ContainerOZovzCR/srcDevDebugProjectContainer.php:391)
  at ContainerOZovzCR\srcDevDebugProjectContainer->load('getDefaultControllerService.php')
     (vendor/symfony/dependency-injection/Container.php:240)
  at Symfony\Component\DependencyInjection\Container->make('App\\Controller\\DefaultController', 1)
     (vendor/symfony/dependency-injection/Container.php:222)
  at Symfony\Component\DependencyInjection\Container->get('App\\Controller\\DefaultController')
     (vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php:51)
  at Symfony\Component\HttpKernel\Controller\ContainerControllerResolver->instantiateController('App\\Controller\\DefaultController')
     (vendor/symfony/framework-bundle/Controller/ControllerResolver.php:54)
  at Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver->instantiateController('App\\Controller\\DefaultController')
     (vendor/symfony/http-kernel/Controller/ControllerResolver.php:110)
  at Symfony\Component\HttpKernel\Controller\ControllerResolver->createController('App\\Controller\\DefaultController::index')
     (vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php:42)
  at Symfony\Component\HttpKernel\Controller\ContainerControllerResolver->createController('App\\Controller\\DefaultController::index')
     (vendor/symfony/framework-bundle/Controller/ControllerResolver.php:46)
  at Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver->createController('App\\Controller\\DefaultController::index')
     (vendor/symfony/http-kernel/Controller/ControllerResolver.php:85)
  at Symfony\Component\HttpKernel\Controller\ControllerResolver->getController(object(Request))
     (vendor/symfony/http-kernel/Controller/TraceableControllerResolver.php:38)
  at Symfony\Component\HttpKernel\Controller\TraceableControllerResolver->getController(object(Request))
     (vendor/symfony/http-kernel/HttpKernel.php:132)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
     (vendor/symfony/http-kernel/HttpKernel.php:66)
  at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
     (vendor/symfony/http-kernel/Kernel.php:188)
  at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
     (public/index.php:37)
digitalray commented 5 years ago

Tried another method of injection but no luck

use Sonata\SeoBundle\SonataSeoBundle;

class DefaultController extends AbstractController
{
    /**
     * @Route("/default", name="default")
     */
    public $seo;

    public function __construct(SonataSeoBundle $seo)
    {
        $this->seo = $seo;
    }

Error: Cannot autowire service "App\Controller\DefaultController": argument "$seo" of method "__construct()" references class "Sonata\SeoBundle\SonataSeoBundle" but no such service exists.

OskarStark commented 5 years ago

You shouldn’t not inject the Bundle, but the service instead: Try SeoPage from Sonata\SeoBubdle\Seo namespace

OskarStark commented 5 years ago

And IMO you should do:

Public function index(SeoPage $seo) ... instead of the constructor

OskarStark commented 5 years ago

Sorry SeoPageInterface should do the trick

digitalray commented 5 years ago

Thanks alot @OskarStark it worked. Apologies for that mistake. It actually autocompleted but I should have caught it. The only other addition that I needed to do to make this work was to add an entry in the config/services.yaml file:

    Sonata\SeoBundle\Seo\SeoPageInterface:
        alias: "sonata.seo.page.default"
        public: true

Here is the Controller

class DefaultController extends AbstractController
{
    /**
     * @Route("/default", name="default")
     */
    public $seo;

    public function __construct(Seo\SeoPageInterface $seo)
    {
        $this->seo = $seo;
    }

    public function index()
    {
        $this->seo->setTitle('test');

        return $this->render('default/index.html.twig', [
            'controller_name' => 'DefaultController',
        ]);
    }
}
OskarStark commented 5 years ago

Great, but why public?

IMO this should work, too:

   Sonata\SeoBundle\Seo\SeoPageInterface:
        alias: "sonata.seo.page.default"

the public is only needed to get it via $this->get('....')

class DefaultController extends AbstractController
{
    public function index(Seo\SeoPageInterface $seo)
    {
        $seo->setTitle('test');

        return $this->render('default/index.html.twig', [
            'controller_name' => 'DefaultController',
        ]);
    }
}
digitalray commented 5 years ago

Thanks again @OskarStark! Removing the public: true was OK. Also I think worked out for the best by putting it in the function instead of the constructor.